Update wuffs to match the version used by Skia

Change-Id: I4c98412464785f1175af68d6d7ae9f8b1038ace2
diff --git a/cmd/ractool/main.go b/cmd/ractool/main.go
new file mode 100644
index 0000000..c552ba3
--- /dev/null
+++ b/cmd/ractool/main.go
@@ -0,0 +1,148 @@
+// Copyright 2019 The Wuffs Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// ----------------
+
+// ractool manipulates Random Access Compression (RAC) files.
+package main
+
+import (
+	"bytes"
+	"errors"
+	"flag"
+	"fmt"
+	"io"
+	"os"
+
+	"github.com/google/wuffs/lib/raczlib"
+)
+
+// TODO: a flag to use a disk-backed (not memory-backed) TempFile.
+
+var (
+	decodeFlag = flag.Bool("decode", false, "whether to decode the input")
+	encodeFlag = flag.Bool("encode", false, "whether to encode the input")
+
+	codecFlag         = flag.String("codec", "zlib", "when encoding, the compression codec")
+	cpagesizeFlag     = flag.Uint64("cpagesize", 0, "when encoding, the page size (in CSpace)")
+	cchunksizeFlag    = flag.Uint64("cchunksize", 0, "when encoding, the chunk size (in CSpace)")
+	dchunksizeFlag    = flag.Uint64("dchunksize", 0, "when encoding, the chunk size (in DSpace)")
+	indexlocationFlag = flag.String("indexlocation", "start",
+		"when encoding, the index location, \"start\" or \"end\"")
+)
+
+const usageStr = `usage: ractool [flags] [input_filename]
+
+If no input_filename is given, stdin is used. Either way, output is written to
+stdout.
+
+The flags should include exactly one of -decode or -encode.
+
+Decoding is not yet implemented.
+
+When encoding, the input is partitioned into chunks and each chunk is
+compressed independently. You can specify the target chunk size in terms of
+either its compressed size or decompressed size. By default (if both
+-cchunksize and -dchunksize are zero), a 64KiB -cchunksize is used.
+
+You can also specify a -cpagesize, which is similar to but not exactly the same
+concept as alignment. If non-zero, padding is inserted into the output to
+minimize the number of pages that each chunk occupies. Look for "CPageSize" in
+the "package rac" documentation for more details.
+
+A RAC file consists of an index and the chunks. The index may be either at the
+start or at the end of the file. See the RAC specification for more details:
+
+https://github.com/google/wuffs/blob/master/doc/spec/rac-spec.md
+
+`
+
+func usage() {
+	fmt.Fprintf(os.Stderr, usageStr)
+	flag.PrintDefaults()
+}
+
+func main() {
+	if err := main1(); err != nil {
+		os.Stderr.WriteString(err.Error() + "\n")
+		os.Exit(1)
+	}
+}
+
+func main1() error {
+	flag.Usage = usage
+	flag.Parse()
+
+	r := io.Reader(os.Stdin)
+	switch flag.NArg() {
+	case 0:
+		// No-op.
+	case 1:
+		f, err := os.Open(flag.Arg(0))
+		if err != nil {
+			return err
+		}
+		defer f.Close()
+		r = f
+	default:
+		return errors.New("too many filenames; the maximum is one")
+	}
+
+	if *decodeFlag && !*encodeFlag {
+		return decode(r)
+	}
+	if *encodeFlag && !*decodeFlag {
+		return encode(r)
+	}
+	return errors.New("must specify exactly one of -decode or -encode")
+}
+
+func decode(r io.Reader) error {
+	return errors.New("TODO: implement a decoder")
+}
+
+func encode(r io.Reader) error {
+	indexLocation := raczlib.IndexLocation(0)
+	switch *indexlocationFlag {
+	case "start":
+		indexLocation = raczlib.IndexLocationAtStart
+	case "end":
+		indexLocation = raczlib.IndexLocationAtEnd
+	default:
+		return errors.New("invalid -indexlocation")
+	}
+
+	if (*cchunksizeFlag != 0) && (*dchunksizeFlag != 0) {
+		return errors.New("must specify none or one of -cchunksize or -dchunksize")
+	} else if (*cchunksizeFlag == 0) && (*dchunksizeFlag == 0) {
+		*cchunksizeFlag = 65536 // 64 KiB.
+	}
+
+	switch *codecFlag {
+	case "zlib":
+		w := &raczlib.Writer{
+			Writer:        os.Stdout,
+			IndexLocation: indexLocation,
+			TempFile:      &bytes.Buffer{},
+			CPageSize:     *cpagesizeFlag,
+			CChunkSize:    *cchunksizeFlag,
+			DChunkSize:    *dchunksizeFlag,
+		}
+		if _, err := io.Copy(w, r); err != nil {
+			return err
+		}
+		return w.Close()
+	}
+	return errors.New("unsupported -codec")
+}
diff --git a/doc/changelog.md b/doc/changelog.md
index 205ad84..3d9501c 100644
--- a/doc/changelog.md
+++ b/doc/changelog.md
@@ -37,6 +37,7 @@
 - Added a `reset` method.
 - Added `peek_uxx`, `skip_fast` and `write_fast_uxx` methods.
 - Renamed some `read_uxx` methods as `read_uxx_as_uyy`.
+- Report image metadata such as ICCP and XMP.
 - Added I/O positions.
 - Added extra fields (uninitialized internal buffers) to structs.
 - Tweaked how marks and limits work.
@@ -68,4 +69,4 @@
 
 ---
 
-Updated on March 2019.
+Updated on April 2019.
diff --git a/doc/note/README.md b/doc/note/README.md
new file mode 100644
index 0000000..d1ec6fd
--- /dev/null
+++ b/doc/note/README.md
@@ -0,0 +1,2 @@
+This directory contains notes: short articles about various Wuffs concepts that
+aren't specific to any one file format or package.
diff --git a/doc/note/base38-and-fourcc.md b/doc/note/base38-and-fourcc.md
new file mode 100644
index 0000000..dc3c83f
--- /dev/null
+++ b/doc/note/base38-and-fourcc.md
@@ -0,0 +1,53 @@
+# Base38 and FourCC Codes
+
+Both of these encode a four-character string such as `"JPEG"` as a `uint32_t`
+value. Computers can compare two integer values faster than they can compare
+two arbitrary strings.
+
+Both schemes maintain ordering: if two four-character strings `s` and `t`
+satisfy `(s < t)`, and those strings have valid numerical encodings, then the
+numerical values also satisfy `(encoding(s) < encoding(t))`.
+
+
+## FourCC
+
+FourCC codes are not specific to Wuffs. For example, the AVI multimedia
+container format can hold various sub-formats, such as "H264" or "YV12",
+distinguished in the overall file format by their FourCC code.
+
+The FourCC encoding is the straightforward sequence of each character's ASCII
+encoding. The FourCC code for `"JPEG"` is `0x4A504547`, since `'J'` is `0x4A`,
+`'P'` is `0x50`, etc. This is essentially 8 bits for each character, 32 bits
+overall. The big-endian representation of this number is exactly the ASCII (and
+UTF-8) string `"JPEG"`.
+
+Other FourCC documentation sometimes use a little-endian convention, so that
+the `{0x4A, 0x50, 0x45, 0x47}` bytes on the wire for `"JPEG"` corresponds to
+the number `0x4745504A` (little-endian) instead of `0x4A504547` (big-endian).
+Wuffs uses the big-endian interpretation, as it maintains ordering.
+
+
+## Base38
+
+Base38 is a tighter encoding than FourCC, fitting four characters into 21 bits
+instead of 32 bits. This is achieved by using a smaller alphabet of 38 possible
+values (space, 0-9, ? or a-z), so that it cannot distinguish between e.g. an
+upper case 'X' and a lower case 'x'. There's also the happy coincidence that
+`38 ** 4` is slightly smaller than `2 ** 21`.
+
+The base38 encoding of `"JPEG"` is `0x122FF6`, which is `1191926`, which is
+`((21 * (38 ** 3)) + (27 * (38 ** 2)) + (16 * (38 ** 1)) + (18 * (38 ** 0)))`.
+
+Using only 21 bits means that we can use base38 values to partition the set of
+possible `uint32_t` values into file-format specific enumerations. Each package
+(i.e. Wuffs implementation of a specific file format) can define up to 1024
+different values in their own namespace, without conflicting with other
+packages (assuming that there aren't e.g. two `"JPEG"` Wuffs packages in the
+same library). The conventional `uint32_t` packing is:
+
+- Bit        31 is reserved (zero).
+- Bits 30 .. 10 are the base38 value, shifted by 10.
+- Bits  9 ..  0 are the enumeration value.
+
+For example, [quirk values](/doc/note/quirks.md) use this `((base38 << 10) |
+enumeration)` scheme.
diff --git a/doc/note/io-input-output.md b/doc/note/io-input-output.md
new file mode 100644
index 0000000..056ae43
--- /dev/null
+++ b/doc/note/io-input-output.md
@@ -0,0 +1,212 @@
+# I/O (Input / Output)
+
+Wuffs per se doesn't have the ability to read or write to files or network
+connections. Recall that Wuffs is a programming language for writing libraries,
+not applications, and that having fewer capabilities means that it's trivial to
+prove that you can't misuse a capability, even when given malicious input.
+
+Instead, the code that calls into Wuffs libraries is responsible for
+interfacing with e.g. the file system or the network system. An `io_buffer` is
+the mechanism for transferring data into and out of Wuffs libraries. For
+example, when decompressing gzip, there are two `io_buffer`'s: the caller fills
+a source buffer with e.g. the compressed file's contents and the callee (the
+Wuffs library) reads compressed bytes from that source buffer and writes
+decompressed bytes to a destination buffer.
+
+
+## I/O Buffers
+
+An `io_buffer` is a [slice](/doc/note/slices-and-tables.md) of bytes (the data,
+a `ptr` and `len`) with additional fields (the metadata): a read index (`ri`),
+a write index (`wi`), a position (`pos`) and whether or not it is `closed`.
+
+
+## Read Index and Write Index
+
+Writing to an `io_buffer`, e.g. copying from a file to a buffer, increments
+`wi`. The buffer is full for writing (no more can be written) when `wi` equals
+`len`. Writing does not have to fill a buffer before further processing.
+
+Reading from an `io_buffer`, e.g. copying from a buffer to a file, increments
+`ri`. The buffer is empty for reading (no more can be read) when `ri` equals
+`wi`. Reading does not have to empty a buffer before further processing.
+
+An invariant condition is that `((0 <= ri) and (ri <= wi) and (wi <= len))`.
+
+Having separate read and write indexes simplifies connecting a sequence of
+filters or processors with `io_buffer`'s, similar to connecting Unix processes
+with pipes. Each filter reads from the previous buffer and writes to the next
+buffer. Each buffer is written to by the previous filter and is read from by
+the next filter. There's no need to flip a buffer between reading and writing
+modes. Nonetheless, `io_buffer`'s are generally not thread-safe.
+
+Continuing the "decompressing gzip" example, the application would write to the
+source buffer by copying from e.g. `/dev/stdin`. The Wuffs library would read
+from the source buffer and write to the destination buffer. The application
+would read from the destination buffer by copying to e.g. `stdout`. Buffer
+space can be re-used, via compaction (see below), so that neither the source
+(`/dev/stdin`) or destination (`/dev/stdout`) data needs to be entirely in
+memory at any point.
+
+For example, an `io_buffer` of length 8 could have 4 bytes available to read
+and 1 byte available to write. If 1 byte was written, there would then be 5
+bytes available to read. Visually:
+
+```
+[.. .. .. .. .. .. .. ..]
+ |<- ri ->|           |  |
+ |<------- wi ------->|  |
+ |<-------- len -------->|
+```
+
+
+## Position
+
+An `io_buffer` is a sliding window into a stream of bytes. Its position (`pos`)
+is the number of bytes in the stream prior to the first element of the slice.
+The total number of bytes read from and written to the stream are therefore
+`(pos + ri)` and `(pos + wi)`.
+
+While every slice element is in-memory, the stream's prior bytes do not
+necessarily have to be in-memory now, or have been in-memory in the past. It is
+valid to open a file, seek to the 1000'th byte and start copying from there to
+an `io_buffer`, provided that `pos` was also initialized to 1000.
+
+
+## Closed-ness
+
+The `closed` field indicates that no further writes are expected to the
+`io_buffer`. When copying from a file to a buffer, `closed` means that we have
+reached EOF (End Of File).
+
+For example, decoding a particular file format might, at some point, expect at
+least another 4 bytes of data, but only 3 are available to read. If `closed` is
+false, this isn't necessarily an error, since an `io_buffer` holds only a
+partial view of the underlying data stream, and more data might be forthcoming
+but not yet buffered. If `closed` is true, it is definitely an error.
+
+
+## Undoing Reads and Writes
+
+It is possible to decrement `ri` or `wi`, undoing previous reads or writes,
+provided that the invariant `((0 <= ri) and (ri <= wi) and (wi <= len))` holds.
+For example, it can be faster on 64 bit (8 byte) systems, if buffer space is
+available, to write 8 bytes and then undo 1 byte than to write exactly 7 bytes.
+
+The Wuffs compiler enforces that, during a Wuffs function, `ri` and `wi` will
+never be decremented (by an undo operation) to be less than the initial values
+at the time of the call. When considering a function as a 'black box', the two
+indexes can only travel forward, and it is up to the application code (not
+Wuffs library) code to rewind the indexes (e.g. by compaction).
+
+Even though `ri` can not drop below its initial value, Wuffs code can still
+read the contents of the slice before `ri` (in sub-slice notation,
+`data[0:ri]`) and it should still contain  the `(pos + 0)`th, `(pos + 1)`th,
+etc. byte of the stream.
+
+The contents of the slice after `wi` (in sub-slice notation, `data[wi:len]`)
+are undefined, and code should not rely on its values. When passing an
+`io_buffer` into a function, that function is free to modify anything in
+`data[wi:len]`, for either value of `wi` before or after the function returns.
+
+
+## Compaction
+
+Compacting an `io_buffer` moves any written but unread bytes (those in
+`data[ri:wi]`) to the start of the buffer, and updates the metadata fields
+`ri`, `wi` and `pos`. Equivalently, it moves the sliding window that is the
+`io_buffer` as far forward as possible along the stream.
+
+This generally increases `(len - wi)`, the number of bytes available for
+writing, allowing for re-using the allocated buffer memory (the data slice).
+
+Suppose that the underlying data stream's `i`th byte has value `i`, and that we
+start with `ri`, `wi` and `pos` were `3`, `7` and `20`. Compaction will
+subtract 3 from the first two and add 3 to the last, so that the new `ri`, `wi`
+and `pos` are `0`, `4` and `23`. Note that `len`, `(pos + ri)` and `(pos + wi)`
+are all unchanged.
+
+Here are two equivalent visualizations of before and after compaction. The `xx`
+means a byte whose value is undefined (as it is at or past `wi`).
+
+The first visualization is where the slice is fixed and its contents (its view
+of the stream) moves relative to the slice:
+
+```
+Before:
+[20 21 22 23 24 25 26 xx]
+ |<- ri ->|           |  |
+ |<------- wi ------->|  |
+ |<-------- len -------->|
+
+After:
+[23 24 25 26 xx xx xx xx]
+ ||          |           |
+ |<-- wi --->|           |
+ |<-------- len -------->|
+```
+
+The second visualization is where the stream (and its contents) is fixed and
+the slice (the sliding window) moves relative to the stream:
+
+```
+                           pos+ri      pos+wi
+                           |           |
+Before:          [20 21 22 23 24 25 26 xx]
+Stream: ... 18 19 20 21 22 23 24 25 26 27 27 28 29 30 31 ...
+After:                    [23 24 25 26 xx xx xx xx]
+                           |           |
+                           pos+ri      pos+wi
+```
+
+
+## Seeking and I/O Positions
+
+Recall that Wuffs code has limited capabilities, and cannot seek in the
+underlying I/O data streams per se. When it needs to seek (e.g. when jumping
+between video frames), it will typically provide an "I/O position", a
+`uint64_t` value, via some package-specific API. The application (the caller of
+the Wuffs code) is then responsible for configuring an `io_buffer` whose
+`(pos + ri)` or `(pos + wi)` value, depending on whether we're reading or
+writing, is at that "I/O position".
+
+If the underlying file (or equivalent) isn't seekable, e.g. it's `/dev/stdin`
+instead of a regular file, then the request cannot be satisfied. The
+application should then decide whether that error is recoverable or fatal. This
+is the application's responsibility, not the library's, as the application
+usually has more context to make that decision.
+
+If that "I/O position" is already within the sliding window, it might not be
+necessary to seek in the underlying file, as it may be possible to e.g. simply
+decrement `ri` to reach a target `(pos + ri)`, for the reading case. Otherwise,
+the typical process is:
+
+1. Set `ri`, `wi` and `pos` to `0`, `0` and that "I/O position". This discards
+   any buffered data (but does not free the buffer's memory).
+2. Seek in the underlying file to that same "I/O position".
+3. Copy from the underlying file to the `io_buffer`, incrementing `wi`.
+
+Whether or not it was necessary to seek and copy from the underlying file, when
+calling back into the Wuffs library, it typically checks that the `io_buffer`'s
+`(pos + ri)` is now at the expected "I/O position".
+
+
+## I/O Reader and I/O Writer
+
+An `io_buffer` is the mechanism for transferring data between the application
+and the Wuffs library. Application code can manipulate an `io_buffer`'s fields
+as it wishes (but is responsible for maintaining the invariant condition).
+Wuffs library code places a further restriction that `io_buffer`s are used
+exclusively either for reading or for writing, as optimizing incremental access
+to an `io_buffer`'s data, while enforcing invariants, is simpler when only one
+of `ri` and `wi` can vary.
+
+Wuffs code therefore refers to either a `base.io_reader` or `base.io_writer`,
+both of which are essentially the same type (an `io_buffer`) with different
+methods. Wuffs code does not reference an `io_buffer` directly.
+
+
+## Binding
+
+TODO: discuss `io_bind`, which temporarily adapts a slice of bytes into an
+`io_buffer`.
diff --git a/doc/note/quirks.md b/doc/note/quirks.md
new file mode 100644
index 0000000..f919da3
--- /dev/null
+++ b/doc/note/quirks.md
@@ -0,0 +1,34 @@
+# Quirks
+
+Quirks are configuration options that let Wuffs more closely match other codec
+implementations.
+
+Some codecs and file formats are more tightly specified than others. When a
+spec doesn't fully dictate how to treat malformed files, different codec
+implementations have disagreed on whether to accept it (e.g. whether they
+follow Postel's Principle of "be liberal in what you accept") and if so, how to
+interpret it. Some implementations also raise those decisions to the
+application level, not the library level: "provide mechanism, not policy".
+
+For example, in an animated GIF image, the first frame might not fill out the
+entire image, and different implementations choose a different default color
+for the outside pixels: opaque black, transparent black, or something else.
+
+Wuffs, out of the box, makes particular choices (typically mimicking the de
+facto canonical implementation), but enabling various quirks result in
+different choices. In particular, quirks are useful in regression testing that
+Wuffs and another implementation produce the same output for the same input,
+even for malformed input.
+
+
+## Wuffs API
+
+Each quirk is assigned a `uint32_t` value, packed using the [base38 namespace
+convention](/doc/note/base38-and-fourcc.md). Decoders and encoders can have a
+`set_quirk_enabled!(quirk base.u32, enabled base.bool)` method whose first
+argument is this `uint32_t` value.
+
+For example, the base38 encoding of `"gif "` is `0xF8586`, so that the
+GIF-specific quirks have a `uint32_t` value of `((0xF8586 << 10) | n)`, for
+some small integer `n`. The generated `C` language file defines human-readable
+names for those constant values.
diff --git a/example/gifplayer/gifplayer.c b/example/gifplayer/gifplayer.c
index 74ff1be..703b968 100644
--- a/example/gifplayer/gifplayer.c
+++ b/example/gifplayer/gifplayer.c
@@ -130,6 +130,7 @@
 const char* reset_color = "\x1B[0m";
 
 bool color_flag = false;
+bool quirk_honor_background_color_flag = false;
 const int stdout_fd = 1;
 
 static inline uint32_t load_u32le(uint8_t* p) {
@@ -138,7 +139,8 @@
 }
 
 void restore_background(wuffs_base__pixel_buffer* pb,
-                        wuffs_base__rect_ie_u32 bounds) {
+                        wuffs_base__rect_ie_u32 bounds,
+                        wuffs_base__color_u32_argb_premul background_color) {
   size_t width = wuffs_base__pixel_config__width(&pb->pixcfg);
   size_t y;
   for (y = bounds.min_incl_y; y < bounds.max_excl_y; y++) {
@@ -146,7 +148,7 @@
     wuffs_base__color_u32_argb_premul* d =
         curr_dst_buffer + (y * width) + bounds.min_incl_x;
     for (x = bounds.min_incl_x; x < bounds.max_excl_x; x++) {
-      *d++ = 0;
+      *d++ = background_color;
     }
   }
 }
@@ -296,6 +298,11 @@
     return status;
   }
 
+  if (quirk_honor_background_color_flag) {
+    wuffs_gif__decoder__set_quirk_enabled(
+        &dec, wuffs_gif__quirk_honor_background_color, true);
+  }
+
   wuffs_base__io_buffer src;
   src.data.ptr = src_buffer;
   src.data.len = src_len;
@@ -303,11 +310,10 @@
   src.meta.ri = 0;
   src.meta.pos = 0;
   src.meta.closed = true;
-  wuffs_base__io_reader src_reader = wuffs_base__io_buffer__reader(&src);
 
   if (first_play) {
     wuffs_base__image_config ic = {0};
-    status = wuffs_gif__decoder__decode_image_config(&dec, &ic, src_reader);
+    status = wuffs_gif__decoder__decode_image_config(&dec, &ic, &src);
     if (status) {
       return status;
     }
@@ -334,13 +340,12 @@
       return status;
     }
     memset(pixbuf.ptr, 0, pixbuf.len);
-    memset(curr_dst_buffer, 0, dst_len);
   }
 
   while (1) {
     wuffs_base__frame_config fc = {0};
     wuffs_base__status status =
-        wuffs_gif__decoder__decode_frame_config(&dec, &fc, src_reader);
+        wuffs_gif__decoder__decode_frame_config(&dec, &fc, &src);
     if (status) {
       if (status == wuffs_base__warning__end_of_data) {
         break;
@@ -348,6 +353,16 @@
       return status;
     }
 
+    if (wuffs_base__frame_config__index(&fc) == 0) {
+      wuffs_base__color_u32_argb_premul background_color =
+          wuffs_base__frame_config__background_color(&fc);
+      size_t i;
+      size_t n = dst_len / sizeof(wuffs_base__color_u32_argb_premul);
+      for (i = 0; i < n; i++) {
+        curr_dst_buffer[i] = background_color;
+      }
+    }
+
     switch (wuffs_base__frame_config__disposal(&fc)) {
       case WUFFS_BASE__ANIMATION_DISPOSAL__RESTORE_PREVIOUS: {
         memcpy(prev_dst_buffer, curr_dst_buffer, dst_len);
@@ -355,8 +370,7 @@
       }
     }
 
-    status =
-        wuffs_gif__decoder__decode_frame(&dec, &pb, src_reader, workbuf, NULL);
+    status = wuffs_gif__decoder__decode_frame(&dec, &pb, &src, workbuf, NULL);
     if (status) {
       if (status == wuffs_base__warning__end_of_data) {
         break;
@@ -370,7 +384,8 @@
 
     switch (wuffs_base__frame_config__disposal(&fc)) {
       case WUFFS_BASE__ANIMATION_DISPOSAL__RESTORE_BACKGROUND: {
-        restore_background(&pb, wuffs_base__frame_config__bounds(&fc));
+        restore_background(&pb, wuffs_base__frame_config__bounds(&fc),
+                           wuffs_base__frame_config__background_color(&fc));
         break;
       }
       case WUFFS_BASE__ANIMATION_DISPOSAL__RESTORE_PREVIOUS: {
@@ -423,6 +438,9 @@
     if (!strcmp(argv[i], "-color")) {
       color_flag = true;
     }
+    if (!strcmp(argv[i], "-quirk_honor_background_color")) {
+      quirk_honor_background_color_flag = true;
+    }
   }
 
   const char* status = read_stdin();
diff --git a/example/library/library.c b/example/library/library.c
index 8726ca3..f8e258a 100644
--- a/example/library/library.c
+++ b/example/library/library.c
@@ -93,9 +93,6 @@
   src.meta.pos = 0;
   src.meta.closed = true;
 
-  wuffs_base__io_writer dst_writer = wuffs_base__io_buffer__writer(&dst);
-  wuffs_base__io_reader src_reader = wuffs_base__io_buffer__reader(&src);
-
   wuffs_gzip__decoder* dec =
       (wuffs_gzip__decoder*)(calloc(sizeof__wuffs_gzip__decoder(), 1));
   if (!dec) {
@@ -109,7 +106,7 @@
     return status;
   }
   status = wuffs_gzip__decoder__decode_io_writer(
-      dec, dst_writer, src_reader,
+      dec, &dst, &src,
       wuffs_base__make_slice_u8(work_buffer, WORK_BUFFER_SIZE));
   if (status) {
     free(dec);
diff --git a/example/zcat/zcat.c b/example/zcat/zcat.c
index c2bf06a..20d7fe4 100644
--- a/example/zcat/zcat.c
+++ b/example/zcat/zcat.c
@@ -124,8 +124,7 @@
 
     while (true) {
       status = wuffs_gzip__decoder__decode_io_writer(
-          &dec, wuffs_base__io_buffer__writer(&dst),
-          wuffs_base__io_buffer__reader(&src),
+          &dec, &dst, &src,
           wuffs_base__make_slice_u8(work_buffer, WORK_BUFFER_SIZE));
 
       if (dst.meta.wi) {
diff --git a/fuzz/c/fuzzlib/fuzzlib.c b/fuzz/c/fuzzlib/fuzzlib.c
index 03050f9..a91e1e4 100644
--- a/fuzz/c/fuzzlib/fuzzlib.c
+++ b/fuzz/c/fuzzlib/fuzzlib.c
@@ -27,7 +27,7 @@
   *intentional_segfault_ptr = 0;
 }
 
-const char* fuzz(wuffs_base__io_reader src_reader, uint32_t hash);
+const char* fuzz(wuffs_base__io_buffer* src, uint32_t hash);
 
 static const char* llvmFuzzerTestOneInput(const uint8_t* data, size_t size) {
   // Hash input as per https://en.wikipedia.org/wiki/Jenkins_hash_function
@@ -55,7 +55,7 @@
       }),
   });
 
-  const char* msg = fuzz(wuffs_base__io_buffer__reader(&src), hash);
+  const char* msg = fuzz(&src, hash);
   if (msg && strstr(msg, "internal error:")) {
     fprintf(stderr, "internal errors shouldn't occur: \"%s\"\n", msg);
     intentional_segfault();
diff --git a/fuzz/c/std/gif_fuzzer.c b/fuzz/c/std/gif_fuzzer.c
index c3ce2fd..2858c1c 100644
--- a/fuzz/c/std/gif_fuzzer.c
+++ b/fuzz/c/std/gif_fuzzer.c
@@ -48,7 +48,7 @@
 #include "../../../release/c/wuffs-unsupported-snapshot.c"
 #include "../fuzzlib/fuzzlib.c"
 
-const char* fuzz(wuffs_base__io_reader src_reader, uint32_t hash) {
+const char* fuzz(wuffs_base__io_buffer* src, uint32_t hash) {
   const char* ret = NULL;
   wuffs_base__slice_u8 pixbuf = ((wuffs_base__slice_u8){});
   wuffs_base__slice_u8 workbuf = ((wuffs_base__slice_u8){});
@@ -67,7 +67,7 @@
     }
 
     wuffs_base__image_config ic = ((wuffs_base__image_config){});
-    status = wuffs_gif__decoder__decode_image_config(&dec, &ic, src_reader);
+    status = wuffs_gif__decoder__decode_image_config(&dec, &ic, src);
     if (status) {
       ret = status;
       goto exit;
@@ -115,7 +115,7 @@
     bool seen_ok = false;
     while (true) {
       wuffs_base__frame_config fc = ((wuffs_base__frame_config){});
-      status = wuffs_gif__decoder__decode_frame_config(&dec, &fc, src_reader);
+      status = wuffs_gif__decoder__decode_frame_config(&dec, &fc, src);
       if (status) {
         if ((status != wuffs_base__warning__end_of_data) || !seen_ok) {
           ret = status;
@@ -123,8 +123,7 @@
         goto exit;
       }
 
-      status = wuffs_gif__decoder__decode_frame(&dec, &pb, src_reader, workbuf,
-                                                NULL);
+      status = wuffs_gif__decoder__decode_frame(&dec, &pb, src, workbuf, NULL);
 
       wuffs_base__rect_ie_u32 frame_rect =
           wuffs_base__frame_config__bounds(&fc);
diff --git a/fuzz/c/std/zlib_fuzzer.c b/fuzz/c/std/zlib_fuzzer.c
index ffded01..b452582 100644
--- a/fuzz/c/std/zlib_fuzzer.c
+++ b/fuzz/c/std/zlib_fuzzer.c
@@ -60,7 +60,7 @@
 uint8_t work_buffer[1];
 #endif
 
-const char* fuzz(wuffs_base__io_reader src_reader, uint32_t hash) {
+const char* fuzz(wuffs_base__io_buffer* src, uint32_t hash) {
   wuffs_zlib__decoder dec;
   const char* status = wuffs_zlib__decoder__initialize(
       &dec, sizeof dec, WUFFS_VERSION,
@@ -81,11 +81,10 @@
           .len = DST_BUFFER_SIZE,
       }),
   });
-  wuffs_base__io_writer dst_writer = wuffs_base__io_buffer__writer(&dst);
 
   while (true) {
     dst.meta.wi = 0;
-    status = wuffs_zlib__decoder__decode_io_writer(&dec, dst_writer, src_reader,
+    status = wuffs_zlib__decoder__decode_io_writer(&dec, &dst, src,
                                                    ((wuffs_base__slice_u8){
                                                        .ptr = work_buffer,
                                                        .len = WORK_BUFFER_SIZE,
diff --git a/go.mod b/go.mod
index 3279328..4ec9849 100644
--- a/go.mod
+++ b/go.mod
@@ -1,3 +1,3 @@
 module github.com/google/wuffs
 
-go 1.13
+go 1.12
diff --git a/internal/cgen/base/core-public.h b/internal/cgen/base/core-public.h
index dc7e7fb..f642fb7 100644
--- a/internal/cgen/base/core-public.h
+++ b/internal/cgen/base/core-public.h
@@ -197,6 +197,12 @@
 
 // --------
 
+// FourCC constants.
+
+// !! INSERT FourCCs.
+
+// --------
+
 // Flicks are a unit of time. One flick (frame-tick) is 1 / 705_600_000 of a
 // second. See https://github.com/OculusVR/Flicks
 typedef int64_t wuffs_base__flicks;
diff --git a/internal/cgen/base/image-impl.c b/internal/cgen/base/image-impl.c
index 6674c4a..7f62677 100644
--- a/internal/cgen/base/image-impl.c
+++ b/internal/cgen/base/image-impl.c
@@ -29,6 +29,61 @@
 }
 
 static uint64_t  //
+wuffs_base__pixel_swizzler__copy_3_1(wuffs_base__slice_u8 dst,
+                                     wuffs_base__slice_u8 dst_palette,
+                                     wuffs_base__slice_u8 src) {
+  if (dst_palette.len != 1024) {
+    return 0;
+  }
+  size_t dst_len3 = dst.len / 3;
+  size_t len = dst_len3 < src.len ? dst_len3 : src.len;
+  uint8_t* d = dst.ptr;
+  uint8_t* s = src.ptr;
+  size_t n = len;
+
+  // N is the loop unroll count.
+  const int N = 4;
+
+  // The comparison in the while condition is ">", not ">=", because with ">=",
+  // the last 4-byte store could write past the end of the dst slice.
+  //
+  // Each 4-byte store writes one too many bytes, but a subsequent store will
+  // overwrite that with the correct byte. There is always another store,
+  // whether a 4-byte store in this loop or a 1-byte store in the next loop.
+  while (n > N) {
+    wuffs_base__store_u32le(
+        d + (0 * 3),
+        wuffs_base__load_u32le(dst_palette.ptr + ((uint32_t)(s[0]) * 4)));
+    wuffs_base__store_u32le(
+        d + (1 * 3),
+        wuffs_base__load_u32le(dst_palette.ptr + ((uint32_t)(s[1]) * 4)));
+    wuffs_base__store_u32le(
+        d + (2 * 3),
+        wuffs_base__load_u32le(dst_palette.ptr + ((uint32_t)(s[2]) * 4)));
+    wuffs_base__store_u32le(
+        d + (3 * 3),
+        wuffs_base__load_u32le(dst_palette.ptr + ((uint32_t)(s[3]) * 4)));
+
+    s += 1 * N;
+    d += 3 * N;
+    n -= (size_t)(1 * N);
+  }
+
+  while (n >= 1) {
+    uint32_t color =
+        wuffs_base__load_u32le(dst_palette.ptr + ((uint32_t)(s[0]) * 4));
+    d[0] = (uint8_t)(color >> 0);
+    d[1] = (uint8_t)(color >> 8);
+    d[2] = (uint8_t)(color >> 16);
+
+    s += 1 * 1;
+    d += 3 * 1;
+    n -= (size_t)(1 * 1);
+  }
+
+  return len;
+}
+static uint64_t  //
 wuffs_base__pixel_swizzler__copy_4_1(wuffs_base__slice_u8 dst,
                                      wuffs_base__slice_u8 dst_palette,
                                      wuffs_base__slice_u8 src) {
@@ -39,8 +94,9 @@
   size_t len = dst_len4 < src.len ? dst_len4 : src.len;
   uint8_t* d = dst.ptr;
   uint8_t* s = src.ptr;
-
   size_t n = len;
+
+  // N is the loop unroll count.
   const int N = 4;
 
   while (n >= N) {
@@ -98,14 +154,14 @@
   return len4 * 4;
 }
 
-void  //
+wuffs_base__status  //
 wuffs_base__pixel_swizzler__prepare(wuffs_base__pixel_swizzler* p,
                                     wuffs_base__pixel_format dst_format,
                                     wuffs_base__slice_u8 dst_palette,
                                     wuffs_base__pixel_format src_format,
                                     wuffs_base__slice_u8 src_palette) {
   if (!p) {
-    return;
+    return wuffs_base__error__bad_receiver;
   }
 
   // TODO: support many more formats.
@@ -125,6 +181,13 @@
           }
           func = wuffs_base__pixel_swizzler__copy_1_1;
           break;
+        case WUFFS_BASE__PIXEL_FORMAT__BGR:
+          if (wuffs_base__slice_u8__copy_from_slice(dst_palette, src_palette) !=
+              1024) {
+            break;
+          }
+          func = wuffs_base__pixel_swizzler__copy_3_1;
+          break;
         case WUFFS_BASE__PIXEL_FORMAT__BGRA_NONPREMUL:
         case WUFFS_BASE__PIXEL_FORMAT__BGRA_PREMUL:
         case WUFFS_BASE__PIXEL_FORMAT__BGRA_BINARY:
@@ -134,6 +197,13 @@
           }
           func = wuffs_base__pixel_swizzler__copy_4_1;
           break;
+        case WUFFS_BASE__PIXEL_FORMAT__RGB:
+          if (wuffs_base__pixel_swizzler__swap_rgbx_bgrx(dst_palette,
+                                                         src_palette) != 1024) {
+            break;
+          }
+          func = wuffs_base__pixel_swizzler__copy_3_1;
+          break;
         case WUFFS_BASE__PIXEL_FORMAT__RGBA_NONPREMUL:
         case WUFFS_BASE__PIXEL_FORMAT__RGBA_PREMUL:
         case WUFFS_BASE__PIXEL_FORMAT__RGBA_BINARY:
@@ -153,13 +223,15 @@
   }
 
   p->private_impl.func = func;
+  return func ? NULL : wuffs_base__error__unsupported_option;
 }
 
 uint64_t  //
-wuffs_base__pixel_swizzler__swizzle_packed(wuffs_base__pixel_swizzler* p,
-                                           wuffs_base__slice_u8 dst,
-                                           wuffs_base__slice_u8 dst_palette,
-                                           wuffs_base__slice_u8 src) {
+wuffs_base__pixel_swizzler__swizzle_interleaved(
+    const wuffs_base__pixel_swizzler* p,
+    wuffs_base__slice_u8 dst,
+    wuffs_base__slice_u8 dst_palette,
+    wuffs_base__slice_u8 src) {
   if (p && p->private_impl.func) {
     return (*(p->private_impl.func))(dst, dst_palette, src);
   }
diff --git a/internal/cgen/base/image-public.h b/internal/cgen/base/image-public.h
index f452b3b..0a18365 100644
--- a/internal/cgen/base/image-public.h
+++ b/internal/cgen/base/image-public.h
@@ -33,9 +33,9 @@
 //  - bit        20 indicates big-endian/MSB-first (as opposed to little/LSB).
 //  - bit        19 indicates floating point (as opposed to integer).
 //  - bit        18 indicates palette-indexed. The number-of-planes (the next
-//                  field) will be zero, as the format is considered packed,
+//                  field) will be 0, as the format is considered interleaved,
 //                  but the 8-bit N-BGRA color data is stored in plane 3.
-//  - bits 17 .. 16 are the number of planes, minus 1. Zero means packed.
+//  - bits 17 .. 16 are the number of planes, minus 1. Zero means interleaved.
 //  - bits 15 .. 12 encodes the number of bits (depth) in the 3rd channel.
 //  - bits 11 ..  8 encodes the number of bits (depth) in the 2nd channel.
 //  - bits  7 ..  4 encodes the number of bits (depth) in the 1st channel.
@@ -52,14 +52,14 @@
 // channels: cyan, magenta, yellow, black).
 //
 // For direct formats with N > 1 channels, those channels can be laid out in
-// either 1 (packed) or N (planar) planes. For example, RGBA data is usually
-// packed, but YCbCr data is usually planar, due to chroma subsampling (for
-// details, see the wuffs_base__pixel_subsampling type).
+// either 1 (interleaved) or N (planar) planes. For example, RGBA data is
+// usually interleaved, but YCbCr data is usually planar, due to chroma
+// subsampling (for details, see the wuffs_base__pixel_subsampling type).
 //
 // For indexed formats, the palette (always 256 × 4 bytes) holds 8 bits per
 // channel non-alpha-premultiplied BGRA color data. There is only 1 plane (for
-// the index), as the format is considered packed. Plane 0 holds the per-pixel
-// indices. Plane 3 is re-purposed to hold the per-index colors.
+// the index), as the format is considered interleaved. Plane 0 holds the
+// per-pixel indices. Plane 3 is re-purposed to hold the per-index colors.
 //
 // The color field is encoded in 3 bits:
 //  - 0 means                   A (Alpha).
@@ -73,8 +73,8 @@
 //
 // In Wuffs, channels are given in memory order (also known as byte order),
 // regardless of endianness, since the C type for the pixel data is an array of
-// bytes, not an array of uint32_t. For example, packed BGRA with 8 bits per
-// channel means that the bytes in memory are always Blue, Green, Red then
+// bytes, not an array of uint32_t. For example, interleaved BGRA with 8 bits
+// per channel means that the bytes in memory are always Blue, Green, Red then
 // Alpha. On big-endian systems, that is the uint32_t 0xBBGGRRAA. On
 // little-endian, 0xAARRGGBB.
 //
@@ -107,15 +107,15 @@
 //
 // For example, wuffs_base__pixel_format 0x5510BBBB is a natural format for
 // decoding a PNG image - network byte order (also known as big-endian),
-// packed, non-premultiplied alpha - that happens to be 16-bit-depth truecolor
-// with alpha (RGBA). In memory order:
+// interleaved, non-premultiplied alpha - that happens to be 16-bit-depth
+// truecolor with alpha (RGBA). In memory order:
 //
 //  ptr+0  ptr+1  ptr+2  ptr+3  ptr+4  ptr+5  ptr+6  ptr+7
 //  Rhi    Rlo    Ghi    Glo    Bhi    Blo    Ahi    Alo
 //
 // For example, the value wuffs_base__pixel_format 0x40000565 means BGR with no
-// alpha or padding, 5/6/5 bits for blue/green/red, packed 2 bytes per pixel,
-// laid out LSB-first in memory order:
+// alpha or padding, 5/6/5 bits for blue/green/red, interleaved 2 bytes per
+// pixel, laid out LSB-first in memory order:
 //
 //  ptr+0...........  ptr+1...........
 //  MSB          LSB  MSB          LSB
@@ -200,8 +200,8 @@
   return f != 0;
 }
 
-// wuffs_base__pixel_format__bits_per_pixel returns, for packed pixel formats,
-// the number of bits per pixel. It returns 0 for planar pixel formats.
+// wuffs_base__pixel_format__bits_per_pixel returns the number of bits per
+// pixel for interleaved pixel formats, and returns 0 for planar pixel formats.
 static inline uint32_t  //
 wuffs_base__pixel_format__bits_per_pixel(wuffs_base__pixel_format f) {
   if (((f >> 16) & 0x03) != 0) {
@@ -219,7 +219,7 @@
 }
 
 static inline bool  //
-wuffs_base__pixel_format__is_packed(wuffs_base__pixel_format f) {
+wuffs_base__pixel_format__is_interleaved(wuffs_base__pixel_format f) {
   return ((f >> 16) & 0x03) == 0;
 }
 
@@ -245,7 +245,7 @@
 // plane p. For a depth of 8 bits (1 byte), the p'th plane's sample starts at
 // (planes[p].ptr + (j * planes[p].stride) + i).
 //
-// For packed pixel formats, the mapping is trivial: i = x and j = y. For
+// For interleaved pixel formats, the mapping is trivial: i = x and j = y. For
 // planar pixel formats, the mapping can differ due to chroma subsampling. For
 // example, consider a three plane YCbCr pixel format with 4:2:2 subsampling.
 // For the luma (Y) channel, there is one sample for every pixel, but for the
@@ -336,13 +336,13 @@
                   uint32_t width,
                   uint32_t height);
   inline void invalidate();
-  inline bool is_valid();
-  inline wuffs_base__pixel_format pixel_format();
-  inline wuffs_base__pixel_subsampling pixel_subsampling();
-  inline wuffs_base__rect_ie_u32 bounds();
-  inline uint32_t width();
-  inline uint32_t height();
-  inline uint64_t pixbuf_len();
+  inline bool is_valid() const;
+  inline wuffs_base__pixel_format pixel_format() const;
+  inline wuffs_base__pixel_subsampling pixel_subsampling() const;
+  inline wuffs_base__rect_ie_u32 bounds() const;
+  inline uint32_t width() const;
+  inline uint32_t height() const;
+  inline uint64_t pixbuf_len() const;
 #endif  // __cplusplus
 
 } wuffs_base__pixel_config;
@@ -396,22 +396,22 @@
 }
 
 static inline bool  //
-wuffs_base__pixel_config__is_valid(wuffs_base__pixel_config* c) {
+wuffs_base__pixel_config__is_valid(const wuffs_base__pixel_config* c) {
   return c && c->private_impl.pixfmt;
 }
 
 static inline wuffs_base__pixel_format  //
-wuffs_base__pixel_config__pixel_format(wuffs_base__pixel_config* c) {
+wuffs_base__pixel_config__pixel_format(const wuffs_base__pixel_config* c) {
   return c ? c->private_impl.pixfmt : 0;
 }
 
 static inline wuffs_base__pixel_subsampling  //
-wuffs_base__pixel_config__pixel_subsampling(wuffs_base__pixel_config* c) {
+wuffs_base__pixel_config__pixel_subsampling(const wuffs_base__pixel_config* c) {
   return c ? c->private_impl.pixsub : 0;
 }
 
 static inline wuffs_base__rect_ie_u32  //
-wuffs_base__pixel_config__bounds(wuffs_base__pixel_config* c) {
+wuffs_base__pixel_config__bounds(const wuffs_base__pixel_config* c) {
   if (c) {
     wuffs_base__rect_ie_u32 ret;
     ret.min_incl_x = 0;
@@ -430,20 +430,20 @@
 }
 
 static inline uint32_t  //
-wuffs_base__pixel_config__width(wuffs_base__pixel_config* c) {
+wuffs_base__pixel_config__width(const wuffs_base__pixel_config* c) {
   return c ? c->private_impl.width : 0;
 }
 
 static inline uint32_t  //
-wuffs_base__pixel_config__height(wuffs_base__pixel_config* c) {
+wuffs_base__pixel_config__height(const wuffs_base__pixel_config* c) {
   return c ? c->private_impl.height : 0;
 }
 
-// TODO: this is the right API for planar (not packed) pixbufs? Should it allow
-// decoding into a color model different from the format's intrinsic one? For
-// example, decoding a JPEG image straight to RGBA instead of to YCbCr?
+// TODO: this is the right API for planar (not interleaved) pixbufs? Should it
+// allow decoding into a color model different from the format's intrinsic one?
+// For example, decoding a JPEG image straight to RGBA instead of to YCbCr?
 static inline uint64_t  //
-wuffs_base__pixel_config__pixbuf_len(wuffs_base__pixel_config* c) {
+wuffs_base__pixel_config__pixbuf_len(const wuffs_base__pixel_config* c) {
   if (!c) {
     return 0;
   }
@@ -492,37 +492,37 @@
 }
 
 inline bool  //
-wuffs_base__pixel_config::is_valid() {
+wuffs_base__pixel_config::is_valid() const {
   return wuffs_base__pixel_config__is_valid(this);
 }
 
 inline wuffs_base__pixel_format  //
-wuffs_base__pixel_config::pixel_format() {
+wuffs_base__pixel_config::pixel_format() const {
   return wuffs_base__pixel_config__pixel_format(this);
 }
 
 inline wuffs_base__pixel_subsampling  //
-wuffs_base__pixel_config::pixel_subsampling() {
+wuffs_base__pixel_config::pixel_subsampling() const {
   return wuffs_base__pixel_config__pixel_subsampling(this);
 }
 
 inline wuffs_base__rect_ie_u32  //
-wuffs_base__pixel_config::bounds() {
+wuffs_base__pixel_config::bounds() const {
   return wuffs_base__pixel_config__bounds(this);
 }
 
 inline uint32_t  //
-wuffs_base__pixel_config::width() {
+wuffs_base__pixel_config::width() const {
   return wuffs_base__pixel_config__width(this);
 }
 
 inline uint32_t  //
-wuffs_base__pixel_config::height() {
+wuffs_base__pixel_config::height() const {
   return wuffs_base__pixel_config__height(this);
 }
 
 inline uint64_t  //
-wuffs_base__pixel_config::pixbuf_len() {
+wuffs_base__pixel_config::pixbuf_len() const {
   return wuffs_base__pixel_config__pixbuf_len(this);
 }
 
@@ -548,9 +548,9 @@
                   uint64_t first_frame_io_position,
                   bool first_frame_is_opaque);
   inline void invalidate();
-  inline bool is_valid();
-  inline uint64_t first_frame_io_position();
-  inline bool first_frame_is_opaque();
+  inline bool is_valid() const;
+  inline uint64_t first_frame_io_position() const;
+  inline bool first_frame_is_opaque() const;
 #endif  // __cplusplus
 
 } wuffs_base__image_config;
@@ -607,17 +607,19 @@
 }
 
 static inline bool  //
-wuffs_base__image_config__is_valid(wuffs_base__image_config* c) {
+wuffs_base__image_config__is_valid(const wuffs_base__image_config* c) {
   return c && wuffs_base__pixel_config__is_valid(&(c->pixcfg));
 }
 
 static inline uint64_t  //
-wuffs_base__image_config__first_frame_io_position(wuffs_base__image_config* c) {
+wuffs_base__image_config__first_frame_io_position(
+    const wuffs_base__image_config* c) {
   return c ? c->private_impl.first_frame_io_position : 0;
 }
 
 static inline bool  //
-wuffs_base__image_config__first_frame_is_opaque(wuffs_base__image_config* c) {
+wuffs_base__image_config__first_frame_is_opaque(
+    const wuffs_base__image_config* c) {
   return c ? c->private_impl.first_frame_is_opaque : false;
 }
 
@@ -640,17 +642,17 @@
 }
 
 inline bool  //
-wuffs_base__image_config::is_valid() {
+wuffs_base__image_config::is_valid() const {
   return wuffs_base__image_config__is_valid(this);
 }
 
 inline uint64_t  //
-wuffs_base__image_config::first_frame_io_position() {
+wuffs_base__image_config::first_frame_io_position() const {
   return wuffs_base__image_config__first_frame_io_position(this);
 }
 
 inline bool  //
-wuffs_base__image_config::first_frame_is_opaque() {
+wuffs_base__image_config::first_frame_is_opaque() const {
   return wuffs_base__image_config__first_frame_is_opaque(this);
 }
 
@@ -706,6 +708,7 @@
     uint64_t io_position;
     wuffs_base__animation_blend blend;
     wuffs_base__animation_disposal disposal;
+    wuffs_base__color_u32_argb_premul background_color;
   } private_impl;
 
 #ifdef __cplusplus
@@ -714,15 +717,17 @@
                      uint64_t index,
                      uint64_t io_position,
                      wuffs_base__animation_blend blend,
-                     wuffs_base__animation_disposal disposal);
-  inline wuffs_base__rect_ie_u32 bounds();
-  inline uint32_t width();
-  inline uint32_t height();
-  inline wuffs_base__flicks duration();
-  inline uint64_t index();
-  inline uint64_t io_position();
-  inline wuffs_base__animation_blend blend();
-  inline wuffs_base__animation_disposal disposal();
+                     wuffs_base__animation_disposal disposal,
+                     wuffs_base__color_u32_argb_premul background_color);
+  inline wuffs_base__rect_ie_u32 bounds() const;
+  inline uint32_t width() const;
+  inline uint32_t height() const;
+  inline wuffs_base__flicks duration() const;
+  inline uint64_t index() const;
+  inline uint64_t io_position() const;
+  inline wuffs_base__animation_blend blend() const;
+  inline wuffs_base__animation_disposal disposal() const;
+  inline wuffs_base__color_u32_argb_premul background_color() const;
 #endif  // __cplusplus
 
 } wuffs_base__frame_config;
@@ -740,13 +745,15 @@
 }
 
 static inline void  //
-wuffs_base__frame_config__update(wuffs_base__frame_config* c,
-                                 wuffs_base__rect_ie_u32 bounds,
-                                 wuffs_base__flicks duration,
-                                 uint64_t index,
-                                 uint64_t io_position,
-                                 wuffs_base__animation_blend blend,
-                                 wuffs_base__animation_disposal disposal) {
+wuffs_base__frame_config__update(
+    wuffs_base__frame_config* c,
+    wuffs_base__rect_ie_u32 bounds,
+    wuffs_base__flicks duration,
+    uint64_t index,
+    uint64_t io_position,
+    wuffs_base__animation_blend blend,
+    wuffs_base__animation_disposal disposal,
+    wuffs_base__color_u32_argb_premul background_color) {
   if (!c) {
     return;
   }
@@ -757,10 +764,11 @@
   c->private_impl.io_position = io_position;
   c->private_impl.blend = blend;
   c->private_impl.disposal = disposal;
+  c->private_impl.background_color = background_color;
 }
 
 static inline wuffs_base__rect_ie_u32  //
-wuffs_base__frame_config__bounds(wuffs_base__frame_config* c) {
+wuffs_base__frame_config__bounds(const wuffs_base__frame_config* c) {
   if (c) {
     return c->private_impl.bounds;
   }
@@ -774,103 +782,115 @@
 }
 
 static inline uint32_t  //
-wuffs_base__frame_config__width(wuffs_base__frame_config* c) {
+wuffs_base__frame_config__width(const wuffs_base__frame_config* c) {
   return c ? wuffs_base__rect_ie_u32__width(&c->private_impl.bounds) : 0;
 }
 
 static inline uint32_t  //
-wuffs_base__frame_config__height(wuffs_base__frame_config* c) {
+wuffs_base__frame_config__height(const wuffs_base__frame_config* c) {
   return c ? wuffs_base__rect_ie_u32__height(&c->private_impl.bounds) : 0;
 }
 
 // wuffs_base__frame_config__duration returns the amount of time to display
 // this frame. Zero means to display forever - a still (non-animated) image.
 static inline wuffs_base__flicks  //
-wuffs_base__frame_config__duration(wuffs_base__frame_config* c) {
+wuffs_base__frame_config__duration(const wuffs_base__frame_config* c) {
   return c ? c->private_impl.duration : 0;
 }
 
 // wuffs_base__frame_config__index returns the index of this frame. The first
 // frame in an image has index 0, the second frame has index 1, and so on.
 static inline uint64_t  //
-wuffs_base__frame_config__index(wuffs_base__frame_config* c) {
+wuffs_base__frame_config__index(const wuffs_base__frame_config* c) {
   return c ? c->private_impl.index : 0;
 }
 
 // wuffs_base__frame_config__io_position returns the I/O stream position before
 // the frame config.
 static inline uint64_t  //
-wuffs_base__frame_config__io_position(wuffs_base__frame_config* c) {
+wuffs_base__frame_config__io_position(const wuffs_base__frame_config* c) {
   return c ? c->private_impl.io_position : 0;
 }
 
 // wuffs_base__frame_config__blend returns, for an animated image, how to blend
 // the transparent pixels of this frame with the existing canvas.
 static inline wuffs_base__animation_blend  //
-wuffs_base__frame_config__blend(wuffs_base__frame_config* c) {
+wuffs_base__frame_config__blend(const wuffs_base__frame_config* c) {
   return c ? c->private_impl.blend : 0;
 }
 
 // wuffs_base__frame_config__disposal returns, for an animated image, how to
 // dispose of this frame after displaying it.
 static inline wuffs_base__animation_disposal  //
-wuffs_base__frame_config__disposal(wuffs_base__frame_config* c) {
+wuffs_base__frame_config__disposal(const wuffs_base__frame_config* c) {
   return c ? c->private_impl.disposal : 0;
 }
 
+static inline wuffs_base__color_u32_argb_premul  //
+wuffs_base__frame_config__background_color(const wuffs_base__frame_config* c) {
+  return c ? c->private_impl.background_color : 0;
+}
+
 #ifdef __cplusplus
 
 inline void  //
-wuffs_base__frame_config::update(wuffs_base__rect_ie_u32 bounds,
-                                 wuffs_base__flicks duration,
-                                 uint64_t index,
-                                 uint64_t io_position,
-                                 wuffs_base__animation_blend blend,
-                                 wuffs_base__animation_disposal disposal) {
+wuffs_base__frame_config::update(
+    wuffs_base__rect_ie_u32 bounds,
+    wuffs_base__flicks duration,
+    uint64_t index,
+    uint64_t io_position,
+    wuffs_base__animation_blend blend,
+    wuffs_base__animation_disposal disposal,
+    wuffs_base__color_u32_argb_premul background_color) {
   wuffs_base__frame_config__update(this, bounds, duration, index, io_position,
-                                   blend, disposal);
+                                   blend, disposal, background_color);
 }
 
 inline wuffs_base__rect_ie_u32  //
-wuffs_base__frame_config::bounds() {
+wuffs_base__frame_config::bounds() const {
   return wuffs_base__frame_config__bounds(this);
 }
 
 inline uint32_t  //
-wuffs_base__frame_config::width() {
+wuffs_base__frame_config::width() const {
   return wuffs_base__frame_config__width(this);
 }
 
 inline uint32_t  //
-wuffs_base__frame_config::height() {
+wuffs_base__frame_config::height() const {
   return wuffs_base__frame_config__height(this);
 }
 
 inline wuffs_base__flicks  //
-wuffs_base__frame_config::duration() {
+wuffs_base__frame_config::duration() const {
   return wuffs_base__frame_config__duration(this);
 }
 
 inline uint64_t  //
-wuffs_base__frame_config::index() {
+wuffs_base__frame_config::index() const {
   return wuffs_base__frame_config__index(this);
 }
 
 inline uint64_t  //
-wuffs_base__frame_config::io_position() {
+wuffs_base__frame_config::io_position() const {
   return wuffs_base__frame_config__io_position(this);
 }
 
 inline wuffs_base__animation_blend  //
-wuffs_base__frame_config::blend() {
+wuffs_base__frame_config::blend() const {
   return wuffs_base__frame_config__blend(this);
 }
 
 inline wuffs_base__animation_disposal  //
-wuffs_base__frame_config::disposal() {
+wuffs_base__frame_config::disposal() const {
   return wuffs_base__frame_config__disposal(this);
 }
 
+inline wuffs_base__color_u32_argb_premul  //
+wuffs_base__frame_config::background_color() const {
+  return wuffs_base__frame_config__background_color(this);
+}
+
 #endif  // __cplusplus
 
 // --------
@@ -888,8 +908,10 @@
 #ifdef __cplusplus
   inline wuffs_base__status set_from_slice(wuffs_base__pixel_config* pixcfg,
                                            wuffs_base__slice_u8 pixbuf_memory);
+  inline wuffs_base__status set_from_table(wuffs_base__pixel_config* pixcfg,
+                                           wuffs_base__table_u8 pixbuf_memory);
   inline wuffs_base__slice_u8 palette();
-  inline wuffs_base__pixel_format pixel_format();
+  inline wuffs_base__pixel_format pixel_format() const;
   inline wuffs_base__table_u8 plane(uint32_t p);
 #endif  // __cplusplus
 
@@ -919,12 +941,13 @@
   }
   if (wuffs_base__pixel_format__is_planar(pixcfg->private_impl.pixfmt)) {
     // TODO: support planar pixel formats, concious of pixel subsampling.
-    return wuffs_base__error__bad_argument;
+    return wuffs_base__error__unsupported_option;
   }
   uint32_t bits_per_pixel =
       wuffs_base__pixel_format__bits_per_pixel(pixcfg->private_impl.pixfmt);
   if ((bits_per_pixel == 0) || ((bits_per_pixel % 8) != 0)) {
-    return wuffs_base__error__bad_argument;
+    // TODO: support fraction-of-byte pixels, e.g. 1 bit per pixel?
+    return wuffs_base__error__unsupported_option;
   }
   uint64_t bytes_per_pixel = bits_per_pixel / 8;
 
@@ -970,6 +993,38 @@
   return NULL;
 }
 
+static inline wuffs_base__status  //
+wuffs_base__pixel_buffer__set_from_table(wuffs_base__pixel_buffer* b,
+                                         wuffs_base__pixel_config* pixcfg,
+                                         wuffs_base__table_u8 pixbuf_memory) {
+  if (!b) {
+    return wuffs_base__error__bad_receiver;
+  }
+  memset(b, 0, sizeof(*b));
+  if (!pixcfg ||
+      wuffs_base__pixel_format__is_planar(pixcfg->private_impl.pixfmt)) {
+    return wuffs_base__error__bad_argument;
+  }
+  uint32_t bits_per_pixel =
+      wuffs_base__pixel_format__bits_per_pixel(pixcfg->private_impl.pixfmt);
+  if ((bits_per_pixel == 0) || ((bits_per_pixel % 8) != 0)) {
+    // TODO: support fraction-of-byte pixels, e.g. 1 bit per pixel?
+    return wuffs_base__error__unsupported_option;
+  }
+  uint64_t bytes_per_pixel = bits_per_pixel / 8;
+
+  uint64_t width_in_bytes =
+      ((uint64_t)pixcfg->private_impl.width) * bytes_per_pixel;
+  if ((width_in_bytes > pixbuf_memory.width) ||
+      (pixcfg->private_impl.height > pixbuf_memory.height)) {
+    return wuffs_base__error__bad_argument;
+  }
+
+  b->pixcfg = *pixcfg;
+  b->private_impl.planes[0] = pixbuf_memory;
+  return NULL;
+}
+
 // wuffs_base__pixel_buffer__palette returns the palette color data. If
 // non-empty, it will have length 1024.
 static inline wuffs_base__slice_u8  //
@@ -986,7 +1041,7 @@
 }
 
 static inline wuffs_base__pixel_format  //
-wuffs_base__pixel_buffer__pixel_format(wuffs_base__pixel_buffer* b) {
+wuffs_base__pixel_buffer__pixel_format(const wuffs_base__pixel_buffer* b) {
   if (b) {
     return b->pixcfg.private_impl.pixfmt;
   }
@@ -1015,13 +1070,19 @@
   return wuffs_base__pixel_buffer__set_from_slice(this, pixcfg, pixbuf_memory);
 }
 
+inline wuffs_base__status  //
+wuffs_base__pixel_buffer::set_from_table(wuffs_base__pixel_config* pixcfg,
+                                         wuffs_base__table_u8 pixbuf_memory) {
+  return wuffs_base__pixel_buffer__set_from_table(this, pixcfg, pixbuf_memory);
+}
+
 inline wuffs_base__slice_u8  //
 wuffs_base__pixel_buffer::palette() {
   return wuffs_base__pixel_buffer__palette(this);
 }
 
 inline wuffs_base__pixel_format  //
-wuffs_base__pixel_buffer::pixel_format() {
+wuffs_base__pixel_buffer::pixel_format() const {
   return wuffs_base__pixel_buffer__pixel_format(this);
 }
 
@@ -1063,20 +1124,18 @@
   } private_impl;
 
 #ifdef __cplusplus
-  inline void prepare(wuffs_base__pixel_format dst_format,
-                      wuffs_base__slice_u8 dst_palette,
-                      wuffs_base__pixel_format src_format,
-                      wuffs_base__slice_u8 src_palette);
-  inline uint64_t swizzle_packed(wuffs_base__slice_u8 dst,
-                                 wuffs_base__slice_u8 dst_palette,
-                                 wuffs_base__slice_u8 src);
+  inline wuffs_base__status prepare(wuffs_base__pixel_format dst_format,
+                                    wuffs_base__slice_u8 dst_palette,
+                                    wuffs_base__pixel_format src_format,
+                                    wuffs_base__slice_u8 src_palette);
+  inline uint64_t swizzle_interleaved(wuffs_base__slice_u8 dst,
+                                      wuffs_base__slice_u8 dst_palette,
+                                      wuffs_base__slice_u8 src) const;
 #endif  // __cplusplus
 
 } wuffs_base__pixel_swizzler;
 
-// TODO: should prepare (both the C and C++ methods) return a status?
-
-void  //
+wuffs_base__status  //
 wuffs_base__pixel_swizzler__prepare(wuffs_base__pixel_swizzler* p,
                                     wuffs_base__pixel_format dst_format,
                                     wuffs_base__slice_u8 dst_palette,
@@ -1084,28 +1143,30 @@
                                     wuffs_base__slice_u8 src_palette);
 
 uint64_t  //
-wuffs_base__pixel_swizzler__swizzle_packed(wuffs_base__pixel_swizzler* p,
-                                           wuffs_base__slice_u8 dst,
-                                           wuffs_base__slice_u8 dst_palette,
-                                           wuffs_base__slice_u8 src);
+wuffs_base__pixel_swizzler__swizzle_interleaved(
+    const wuffs_base__pixel_swizzler* p,
+    wuffs_base__slice_u8 dst,
+    wuffs_base__slice_u8 dst_palette,
+    wuffs_base__slice_u8 src);
 
 #ifdef __cplusplus
 
-inline void  //
+inline wuffs_base__status  //
 wuffs_base__pixel_swizzler::prepare(wuffs_base__pixel_format dst_format,
                                     wuffs_base__slice_u8 dst_palette,
                                     wuffs_base__pixel_format src_format,
                                     wuffs_base__slice_u8 src_palette) {
-  wuffs_base__pixel_swizzler__prepare(this, dst_format, dst_palette, src_format,
-                                      src_palette);
+  return wuffs_base__pixel_swizzler__prepare(this, dst_format, dst_palette,
+                                             src_format, src_palette);
 }
 
 uint64_t  //
-wuffs_base__pixel_swizzler::swizzle_packed(wuffs_base__slice_u8 dst,
-                                           wuffs_base__slice_u8 dst_palette,
-                                           wuffs_base__slice_u8 src) {
-  return wuffs_base__pixel_swizzler__swizzle_packed(this, dst, dst_palette,
-                                                    src);
+wuffs_base__pixel_swizzler::swizzle_interleaved(
+    wuffs_base__slice_u8 dst,
+    wuffs_base__slice_u8 dst_palette,
+    wuffs_base__slice_u8 src) const {
+  return wuffs_base__pixel_swizzler__swizzle_interleaved(this, dst, dst_palette,
+                                                         src);
 }
 
 #endif  // __cplusplus
diff --git a/internal/cgen/base/io-private.h b/internal/cgen/base/io-private.h
index 088f3ce..fad529b 100644
--- a/internal/cgen/base/io-private.h
+++ b/internal/cgen/base/io-private.h
@@ -16,65 +16,52 @@
 
 // ---------------- I/O
 
-static inline bool  //
-wuffs_base__io_buffer__is_valid(wuffs_base__io_buffer buf) {
-  return (buf.data.ptr || (buf.data.len == 0)) &&
-         (buf.data.len >= buf.meta.wi) && (buf.meta.wi >= buf.meta.ri);
-}
-
-// TODO: wuffs_base__io_reader__is_eof is no longer used by Wuffs per se, but
-// it might be handy to programs that use Wuffs. Either delete it, or promote
-// it to the public API.
+// "Null" as in "/dev/null", not as in "nullptr".
 //
-// If making this function public (i.e. moving it to base-header.h), it also
-// needs to allow NULL (i.e. implicit, callee-calculated) mark/limit.
+// TODO: ensure that this is zero-initialized.
+static wuffs_base__io_buffer wuffs_base__global__null_io_buffer;
 
-static inline bool  //
-wuffs_base__io_reader__is_eof(wuffs_base__io_reader o) {
-  wuffs_base__io_buffer* buf = o.private_impl.buf;
-  return buf && buf->meta.closed &&
-         (buf->data.ptr + buf->meta.wi == o.private_impl.limit);
+static inline wuffs_base__io_buffer*  //
+wuffs_base__null_io_reader() {
+  return &wuffs_base__global__null_io_buffer;
 }
 
-static inline bool  //
-wuffs_base__io_reader__is_valid(wuffs_base__io_reader o) {
-  wuffs_base__io_buffer* buf = o.private_impl.buf;
-  // Note: if making this function public (i.e. moving it to base-header.h), it
-  // also needs to allow NULL (i.e. implicit, callee-calculated) mark/limit.
-  return buf ? ((buf->data.ptr <= o.private_impl.mark) &&
-                (o.private_impl.mark <= o.private_impl.limit) &&
-                (o.private_impl.limit <= buf->data.ptr + buf->data.len))
-             : ((o.private_impl.mark == NULL) &&
-                (o.private_impl.limit == NULL));
+static inline wuffs_base__io_buffer*  //
+wuffs_base__null_io_writer() {
+  return &wuffs_base__global__null_io_buffer;
 }
 
-static inline bool  //
-wuffs_base__io_writer__is_valid(wuffs_base__io_writer o) {
-  wuffs_base__io_buffer* buf = o.private_impl.buf;
-  // Note: if making this function public (i.e. moving it to base-header.h), it
-  // also needs to allow NULL (i.e. implicit, callee-calculated) mark/limit.
-  return buf ? ((buf->data.ptr <= o.private_impl.mark) &&
-                (o.private_impl.mark <= o.private_impl.limit) &&
-                (o.private_impl.limit <= buf->data.ptr + buf->data.len))
-             : ((o.private_impl.mark == NULL) &&
-                (o.private_impl.limit == NULL));
+static inline uint64_t  //
+wuffs_base__io__count_since(uint64_t mark, uint64_t index) {
+  if (index >= mark) {
+    return index - mark;
+  }
+  return 0;
+}
+
+static inline wuffs_base__slice_u8  //
+wuffs_base__io__since(uint64_t mark, uint64_t index, uint8_t* ptr) {
+  if (index >= mark) {
+    return wuffs_base__make_slice_u8(ptr + mark, index - mark);
+  }
+  return wuffs_base__make_slice_u8(NULL, 0);
 }
 
 static inline uint32_t  //
 wuffs_base__io_writer__copy_n_from_history(uint8_t** ptr_iop_w,
-                                           uint8_t* io0_w,
                                            uint8_t* io1_w,
+                                           uint8_t* io2_w,
                                            uint32_t length,
                                            uint32_t distance) {
   if (!distance) {
     return 0;
   }
   uint8_t* p = *ptr_iop_w;
-  if ((size_t)(p - io0_w) < (size_t)(distance)) {
+  if ((size_t)(p - io1_w) < (size_t)(distance)) {
     return 0;
   }
   uint8_t* q = p - distance;
-  size_t n = (size_t)(io1_w - p);
+  size_t n = (size_t)(io2_w - p);
   if ((size_t)(length) > n) {
     length = (uint32_t)(n);
   } else {
@@ -106,12 +93,12 @@
 // wuffs_base__io_writer__copy_n_from_history function above, but has stronger
 // pre-conditions. The caller needs to prove that:
 //  - distance >  0
-//  - distance <= (*ptr_iop_w - io0_w)
-//  - length   <= (io1_w      - *ptr_iop_w)
+//  - distance <= (*ptr_iop_w - io1_w)
+//  - length   <= (io2_w      - *ptr_iop_w)
 static inline uint32_t  //
 wuffs_base__io_writer__copy_n_from_history_fast(uint8_t** ptr_iop_w,
-                                                uint8_t* io0_w,
                                                 uint8_t* io1_w,
+                                                uint8_t* io2_w,
                                                 uint32_t length,
                                                 uint32_t distance) {
   uint8_t* p = *ptr_iop_w;
@@ -131,18 +118,18 @@
 
 static inline uint32_t  //
 wuffs_base__io_writer__copy_n_from_reader(uint8_t** ptr_iop_w,
-                                          uint8_t* io1_w,
+                                          uint8_t* io2_w,
                                           uint32_t length,
                                           uint8_t** ptr_iop_r,
-                                          uint8_t* io1_r) {
+                                          uint8_t* io2_r) {
   uint8_t* iop_w = *ptr_iop_w;
   size_t n = length;
-  if (n > ((size_t)(io1_w - iop_w))) {
-    n = (size_t)(io1_w - iop_w);
+  if (n > ((size_t)(io2_w - iop_w))) {
+    n = (size_t)(io2_w - iop_w);
   }
   uint8_t* iop_r = *ptr_iop_r;
-  if (n > ((size_t)(io1_r - iop_r))) {
-    n = (size_t)(io1_r - iop_r);
+  if (n > ((size_t)(io2_r - iop_r))) {
+    n = (size_t)(io2_r - iop_r);
   }
   if (n > 0) {
     memmove(iop_w, iop_r, n);
@@ -154,12 +141,12 @@
 
 static inline uint64_t  //
 wuffs_base__io_writer__copy_from_slice(uint8_t** ptr_iop_w,
-                                       uint8_t* io1_w,
+                                       uint8_t* io2_w,
                                        wuffs_base__slice_u8 src) {
   uint8_t* iop_w = *ptr_iop_w;
   size_t n = src.len;
-  if (n > ((size_t)(io1_w - iop_w))) {
-    n = (size_t)(io1_w - iop_w);
+  if (n > ((size_t)(io2_w - iop_w))) {
+    n = (size_t)(io2_w - iop_w);
   }
   if (n > 0) {
     memmove(iop_w, src.ptr, n);
@@ -170,7 +157,7 @@
 
 static inline uint32_t  //
 wuffs_base__io_writer__copy_n_from_slice(uint8_t** ptr_iop_w,
-                                         uint8_t* io1_w,
+                                         uint8_t* io2_w,
                                          uint32_t length,
                                          wuffs_base__slice_u8 src) {
   uint8_t* iop_w = *ptr_iop_w;
@@ -178,8 +165,8 @@
   if (n > length) {
     n = length;
   }
-  if (n > ((size_t)(io1_w - iop_w))) {
-    n = (size_t)(io1_w - iop_w);
+  if (n > ((size_t)(io2_w - iop_w))) {
+    n = (size_t)(io2_w - iop_w);
   }
   if (n > 0) {
     memmove(iop_w, src.ptr, n);
@@ -188,11 +175,12 @@
   return (uint32_t)(n);
 }
 
-static inline wuffs_base__empty_struct  //
-wuffs_base__io_reader__set(wuffs_base__io_reader* o,
-                           wuffs_base__io_buffer* b,
+static inline wuffs_base__io_buffer*  //
+wuffs_base__io_reader__set(wuffs_base__io_buffer* b,
                            uint8_t** ptr_iop_r,
+                           uint8_t** ptr_io0_r,
                            uint8_t** ptr_io1_r,
+                           uint8_t** ptr_io2_r,
                            wuffs_base__slice_u8 data) {
   b->data = data;
   b->meta.wi = data.len;
@@ -200,42 +188,17 @@
   b->meta.pos = 0;
   b->meta.closed = false;
 
-  o->private_impl.buf = b;
-  o->private_impl.mark = data.ptr;
-  o->private_impl.limit = data.ptr + data.len;
   *ptr_iop_r = data.ptr;
-  *ptr_io1_r = data.ptr + data.len;
+  *ptr_io0_r = data.ptr;
+  *ptr_io1_r = data.ptr;
+  *ptr_io2_r = data.ptr + data.len;
 
-  wuffs_base__empty_struct ret;
-  ret.private_impl = 0;
-  return ret;
-}
-
-static inline wuffs_base__empty_struct  //
-wuffs_base__io_reader__set_limit(wuffs_base__io_reader* o,
-                                 uint8_t* iop_r,
-                                 uint64_t limit) {
-  if (o && (((size_t)(o->private_impl.limit - iop_r)) > limit)) {
-    o->private_impl.limit = iop_r + limit;
-  }
-
-  wuffs_base__empty_struct ret;
-  ret.private_impl = 0;
-  return ret;
-}
-
-static inline wuffs_base__empty_struct  //
-wuffs_base__io_reader__set_mark(wuffs_base__io_reader* o, uint8_t* mark) {
-  o->private_impl.mark = mark;
-
-  wuffs_base__empty_struct ret;
-  ret.private_impl = 0;
-  return ret;
+  return b;
 }
 
 static inline wuffs_base__slice_u8  //
-wuffs_base__io_reader__take(uint8_t** ptr_iop_r, uint8_t* io1_r, uint64_t n) {
-  if (n <= ((size_t)(io1_r - *ptr_iop_r))) {
+wuffs_base__io_reader__take(uint8_t** ptr_iop_r, uint8_t* io2_r, uint64_t n) {
+  if (n <= ((size_t)(io2_r - *ptr_iop_r))) {
     uint8_t* p = *ptr_iop_r;
     *ptr_iop_r += n;
     return wuffs_base__make_slice_u8(p, n);
@@ -243,11 +206,12 @@
   return wuffs_base__make_slice_u8(NULL, 0);
 }
 
-static inline wuffs_base__empty_struct  //
-wuffs_base__io_writer__set(wuffs_base__io_writer* o,
-                           wuffs_base__io_buffer* b,
+static inline wuffs_base__io_buffer*  //
+wuffs_base__io_writer__set(wuffs_base__io_buffer* b,
                            uint8_t** ptr_iop_w,
+                           uint8_t** ptr_io0_w,
                            uint8_t** ptr_io1_w,
+                           uint8_t** ptr_io2_w,
                            wuffs_base__slice_u8 data) {
   b->data = data;
   b->meta.wi = 0;
@@ -255,24 +219,12 @@
   b->meta.pos = 0;
   b->meta.closed = false;
 
-  o->private_impl.buf = b;
-  o->private_impl.mark = data.ptr;
-  o->private_impl.limit = data.ptr + data.len;
   *ptr_iop_w = data.ptr;
-  *ptr_io1_w = data.ptr + data.len;
+  *ptr_io0_w = data.ptr;
+  *ptr_io1_w = data.ptr;
+  *ptr_io2_w = data.ptr + data.len;
 
-  wuffs_base__empty_struct ret;
-  ret.private_impl = 0;
-  return ret;
-}
-
-static inline wuffs_base__empty_struct  //
-wuffs_base__io_writer__set_mark(wuffs_base__io_writer* o, uint8_t* mark) {
-  o->private_impl.mark = mark;
-
-  wuffs_base__empty_struct ret;
-  ret.private_impl = 0;
-  return ret;
+  return b;
 }
 
 // ---------------- I/O (Utility)
diff --git a/internal/cgen/base/io-public.h b/internal/cgen/base/io-public.h
index 6e9bb58..271c7f9 100644
--- a/internal/cgen/base/io-public.h
+++ b/internal/cgen/base/io-public.h
@@ -15,34 +15,8 @@
 // limitations under the License.
 
 // ---------------- I/O
-
-struct wuffs_base__io_buffer__struct;
-
-typedef struct {
-  // Do not access the private_impl's fields directly. There is no API/ABI
-  // compatibility or safety guarantee if you do so.
-  struct {
-    struct wuffs_base__io_buffer__struct* buf;
-    // The bounds values are typically NULL, when created by the Wuffs public
-    // API. NULL means that the callee substitutes the implicit bounds derived
-    // from buf.
-    uint8_t* mark;
-    uint8_t* limit;
-  } private_impl;
-} wuffs_base__io_reader;
-
-typedef struct {
-  // Do not access the private_impl's fields directly. There is no API/ABI
-  // compatibility or safety guarantee if you do so.
-  struct {
-    struct wuffs_base__io_buffer__struct* buf;
-    // The bounds values are typically NULL, when created by the Wuffs public
-    // API. NULL means that the callee substitutes the implicit bounds derived
-    // from buf.
-    uint8_t* mark;
-    uint8_t* limit;
-  } private_impl;
-} wuffs_base__io_writer;
+//
+// See (/doc/note/io-input-output.md).
 
 // wuffs_base__io_buffer_meta is the metadata for a wuffs_base__io_buffer's
 // data.
@@ -63,10 +37,12 @@
 
 #ifdef __cplusplus
   inline void compact();
-  inline wuffs_base__io_reader reader();
-  inline wuffs_base__io_writer writer();
-  inline uint64_t reader_io_position();
-  inline uint64_t writer_io_position();
+  inline wuffs_base__io_buffer__struct* reader();  // Deprecated.
+  inline wuffs_base__io_buffer__struct* writer();  // Deprecated.
+  inline uint64_t reader_available() const;
+  inline uint64_t reader_io_position() const;
+  inline uint64_t writer_available() const;
+  inline uint64_t writer_io_position() const;
 #endif  // __cplusplus
 
 } wuffs_base__io_buffer;
@@ -115,24 +91,6 @@
   return ret;
 }
 
-static inline wuffs_base__io_reader  //
-wuffs_base__null_io_reader() {
-  wuffs_base__io_reader ret;
-  ret.private_impl.buf = NULL;
-  ret.private_impl.mark = NULL;
-  ret.private_impl.limit = NULL;
-  return ret;
-}
-
-static inline wuffs_base__io_writer  //
-wuffs_base__null_io_writer() {
-  wuffs_base__io_writer ret;
-  ret.private_impl.buf = NULL;
-  ret.private_impl.mark = NULL;
-  ret.private_impl.limit = NULL;
-  return ret;
-}
-
 // wuffs_base__io_buffer__compact moves any written but unread bytes to the
 // start of the buffer.
 static inline void  //
@@ -149,31 +107,23 @@
   buf->meta.ri = 0;
 }
 
-static inline wuffs_base__io_reader  //
-wuffs_base__io_buffer__reader(wuffs_base__io_buffer* buf) {
-  wuffs_base__io_reader ret;
-  ret.private_impl.buf = buf;
-  ret.private_impl.mark = NULL;
-  ret.private_impl.limit = NULL;
-  return ret;
-}
-
-static inline wuffs_base__io_writer  //
-wuffs_base__io_buffer__writer(wuffs_base__io_buffer* buf) {
-  wuffs_base__io_writer ret;
-  ret.private_impl.buf = buf;
-  ret.private_impl.mark = NULL;
-  ret.private_impl.limit = NULL;
-  return ret;
+static inline uint64_t  //
+wuffs_base__io_buffer__reader_available(const wuffs_base__io_buffer* buf) {
+  return buf ? buf->meta.wi - buf->meta.ri : 0;
 }
 
 static inline uint64_t  //
-wuffs_base__io_buffer__reader_io_position(wuffs_base__io_buffer* buf) {
+wuffs_base__io_buffer__reader_io_position(const wuffs_base__io_buffer* buf) {
   return buf ? wuffs_base__u64__sat_add(buf->meta.pos, buf->meta.ri) : 0;
 }
 
 static inline uint64_t  //
-wuffs_base__io_buffer__writer_io_position(wuffs_base__io_buffer* buf) {
+wuffs_base__io_buffer__writer_available(const wuffs_base__io_buffer* buf) {
+  return buf ? buf->data.len - buf->meta.wi : 0;
+}
+
+static inline uint64_t  //
+wuffs_base__io_buffer__writer_io_position(const wuffs_base__io_buffer* buf) {
   return buf ? wuffs_base__u64__sat_add(buf->meta.pos, buf->meta.wi) : 0;
 }
 
@@ -184,23 +134,33 @@
   wuffs_base__io_buffer__compact(this);
 }
 
-inline wuffs_base__io_reader  //
+inline wuffs_base__io_buffer*  //
 wuffs_base__io_buffer__struct::reader() {
-  return wuffs_base__io_buffer__reader(this);
+  return this;
 }
 
-inline wuffs_base__io_writer  //
+inline wuffs_base__io_buffer*  //
 wuffs_base__io_buffer__struct::writer() {
-  return wuffs_base__io_buffer__writer(this);
+  return this;
 }
 
 inline uint64_t  //
-wuffs_base__io_buffer__struct::reader_io_position() {
+wuffs_base__io_buffer__struct::reader_available() const {
+  return wuffs_base__io_buffer__reader_available(this);
+}
+
+inline uint64_t  //
+wuffs_base__io_buffer__struct::reader_io_position() const {
   return wuffs_base__io_buffer__reader_io_position(this);
 }
 
 inline uint64_t  //
-wuffs_base__io_buffer__struct::writer_io_position() {
+wuffs_base__io_buffer__struct::writer_available() const {
+  return wuffs_base__io_buffer__writer_available(this);
+}
+
+inline uint64_t  //
+wuffs_base__io_buffer__struct::writer_io_position() const {
   return wuffs_base__io_buffer__writer_io_position(this);
 }
 
diff --git a/internal/cgen/base/range-public.h b/internal/cgen/base/range-public.h
index 69d6ecb..b6f6be7 100644
--- a/internal/cgen/base/range-public.h
+++ b/internal/cgen/base/range-public.h
@@ -52,14 +52,14 @@
   uint32_t max_incl;
 
 #ifdef __cplusplus
-  inline bool is_empty();
-  inline bool equals(wuffs_base__range_ii_u32__struct s);
+  inline bool is_empty() const;
+  inline bool equals(wuffs_base__range_ii_u32__struct s) const;
   inline wuffs_base__range_ii_u32__struct intersect(
-      wuffs_base__range_ii_u32__struct s);
+      wuffs_base__range_ii_u32__struct s) const;
   inline wuffs_base__range_ii_u32__struct unite(
-      wuffs_base__range_ii_u32__struct s);
-  inline bool contains(uint32_t x);
-  inline bool contains_range(wuffs_base__range_ii_u32__struct s);
+      wuffs_base__range_ii_u32__struct s) const;
+  inline bool contains(uint32_t x) const;
+  inline bool contains_range(wuffs_base__range_ii_u32__struct s) const;
 #endif  // __cplusplus
 
 } wuffs_base__range_ii_u32;
@@ -73,12 +73,12 @@
 }
 
 static inline bool  //
-wuffs_base__range_ii_u32__is_empty(wuffs_base__range_ii_u32* r) {
+wuffs_base__range_ii_u32__is_empty(const wuffs_base__range_ii_u32* r) {
   return r->min_incl > r->max_incl;
 }
 
 static inline bool  //
-wuffs_base__range_ii_u32__equals(wuffs_base__range_ii_u32* r,
+wuffs_base__range_ii_u32__equals(const wuffs_base__range_ii_u32* r,
                                  wuffs_base__range_ii_u32 s) {
   return (r->min_incl == s.min_incl && r->max_incl == s.max_incl) ||
          (wuffs_base__range_ii_u32__is_empty(r) &&
@@ -86,7 +86,7 @@
 }
 
 static inline wuffs_base__range_ii_u32  //
-wuffs_base__range_ii_u32__intersect(wuffs_base__range_ii_u32* r,
+wuffs_base__range_ii_u32__intersect(const wuffs_base__range_ii_u32* r,
                                     wuffs_base__range_ii_u32 s) {
   wuffs_base__range_ii_u32 t;
   t.min_incl = wuffs_base__u32__max(r->min_incl, s.min_incl);
@@ -95,7 +95,7 @@
 }
 
 static inline wuffs_base__range_ii_u32  //
-wuffs_base__range_ii_u32__unite(wuffs_base__range_ii_u32* r,
+wuffs_base__range_ii_u32__unite(const wuffs_base__range_ii_u32* r,
                                 wuffs_base__range_ii_u32 s) {
   if (wuffs_base__range_ii_u32__is_empty(r)) {
     return s;
@@ -110,12 +110,13 @@
 }
 
 static inline bool  //
-wuffs_base__range_ii_u32__contains(wuffs_base__range_ii_u32* r, uint32_t x) {
+wuffs_base__range_ii_u32__contains(const wuffs_base__range_ii_u32* r,
+                                   uint32_t x) {
   return (r->min_incl <= x) && (x <= r->max_incl);
 }
 
 static inline bool  //
-wuffs_base__range_ii_u32__contains_range(wuffs_base__range_ii_u32* r,
+wuffs_base__range_ii_u32__contains_range(const wuffs_base__range_ii_u32* r,
                                          wuffs_base__range_ii_u32 s) {
   return wuffs_base__range_ii_u32__equals(
       &s, wuffs_base__range_ii_u32__intersect(r, s));
@@ -124,32 +125,32 @@
 #ifdef __cplusplus
 
 inline bool  //
-wuffs_base__range_ii_u32::is_empty() {
+wuffs_base__range_ii_u32::is_empty() const {
   return wuffs_base__range_ii_u32__is_empty(this);
 }
 
 inline bool  //
-wuffs_base__range_ii_u32::equals(wuffs_base__range_ii_u32 s) {
+wuffs_base__range_ii_u32::equals(wuffs_base__range_ii_u32 s) const {
   return wuffs_base__range_ii_u32__equals(this, s);
 }
 
 inline wuffs_base__range_ii_u32  //
-wuffs_base__range_ii_u32::intersect(wuffs_base__range_ii_u32 s) {
+wuffs_base__range_ii_u32::intersect(wuffs_base__range_ii_u32 s) const {
   return wuffs_base__range_ii_u32__intersect(this, s);
 }
 
 inline wuffs_base__range_ii_u32  //
-wuffs_base__range_ii_u32::unite(wuffs_base__range_ii_u32 s) {
+wuffs_base__range_ii_u32::unite(wuffs_base__range_ii_u32 s) const {
   return wuffs_base__range_ii_u32__unite(this, s);
 }
 
 inline bool  //
-wuffs_base__range_ii_u32::contains(uint32_t x) {
+wuffs_base__range_ii_u32::contains(uint32_t x) const {
   return wuffs_base__range_ii_u32__contains(this, x);
 }
 
 inline bool  //
-wuffs_base__range_ii_u32::contains_range(wuffs_base__range_ii_u32 s) {
+wuffs_base__range_ii_u32::contains_range(wuffs_base__range_ii_u32 s) const {
   return wuffs_base__range_ii_u32__contains_range(this, s);
 }
 
@@ -162,15 +163,15 @@
   uint32_t max_excl;
 
 #ifdef __cplusplus
-  inline bool is_empty();
-  inline bool equals(wuffs_base__range_ie_u32__struct s);
+  inline bool is_empty() const;
+  inline bool equals(wuffs_base__range_ie_u32__struct s) const;
   inline wuffs_base__range_ie_u32__struct intersect(
-      wuffs_base__range_ie_u32__struct s);
+      wuffs_base__range_ie_u32__struct s) const;
   inline wuffs_base__range_ie_u32__struct unite(
-      wuffs_base__range_ie_u32__struct s);
-  inline bool contains(uint32_t x);
-  inline bool contains_range(wuffs_base__range_ie_u32__struct s);
-  inline uint32_t length();
+      wuffs_base__range_ie_u32__struct s) const;
+  inline bool contains(uint32_t x) const;
+  inline bool contains_range(wuffs_base__range_ie_u32__struct s) const;
+  inline uint32_t length() const;
 #endif  // __cplusplus
 
 } wuffs_base__range_ie_u32;
@@ -184,12 +185,12 @@
 }
 
 static inline bool  //
-wuffs_base__range_ie_u32__is_empty(wuffs_base__range_ie_u32* r) {
+wuffs_base__range_ie_u32__is_empty(const wuffs_base__range_ie_u32* r) {
   return r->min_incl >= r->max_excl;
 }
 
 static inline bool  //
-wuffs_base__range_ie_u32__equals(wuffs_base__range_ie_u32* r,
+wuffs_base__range_ie_u32__equals(const wuffs_base__range_ie_u32* r,
                                  wuffs_base__range_ie_u32 s) {
   return (r->min_incl == s.min_incl && r->max_excl == s.max_excl) ||
          (wuffs_base__range_ie_u32__is_empty(r) &&
@@ -197,7 +198,7 @@
 }
 
 static inline wuffs_base__range_ie_u32  //
-wuffs_base__range_ie_u32__intersect(wuffs_base__range_ie_u32* r,
+wuffs_base__range_ie_u32__intersect(const wuffs_base__range_ie_u32* r,
                                     wuffs_base__range_ie_u32 s) {
   wuffs_base__range_ie_u32 t;
   t.min_incl = wuffs_base__u32__max(r->min_incl, s.min_incl);
@@ -206,7 +207,7 @@
 }
 
 static inline wuffs_base__range_ie_u32  //
-wuffs_base__range_ie_u32__unite(wuffs_base__range_ie_u32* r,
+wuffs_base__range_ie_u32__unite(const wuffs_base__range_ie_u32* r,
                                 wuffs_base__range_ie_u32 s) {
   if (wuffs_base__range_ie_u32__is_empty(r)) {
     return s;
@@ -221,56 +222,57 @@
 }
 
 static inline bool  //
-wuffs_base__range_ie_u32__contains(wuffs_base__range_ie_u32* r, uint32_t x) {
+wuffs_base__range_ie_u32__contains(const wuffs_base__range_ie_u32* r,
+                                   uint32_t x) {
   return (r->min_incl <= x) && (x < r->max_excl);
 }
 
 static inline bool  //
-wuffs_base__range_ie_u32__contains_range(wuffs_base__range_ie_u32* r,
+wuffs_base__range_ie_u32__contains_range(const wuffs_base__range_ie_u32* r,
                                          wuffs_base__range_ie_u32 s) {
   return wuffs_base__range_ie_u32__equals(
       &s, wuffs_base__range_ie_u32__intersect(r, s));
 }
 
 static inline uint32_t  //
-wuffs_base__range_ie_u32__length(wuffs_base__range_ie_u32* r) {
+wuffs_base__range_ie_u32__length(const wuffs_base__range_ie_u32* r) {
   return wuffs_base__u32__sat_sub(r->max_excl, r->min_incl);
 }
 
 #ifdef __cplusplus
 
 inline bool  //
-wuffs_base__range_ie_u32::is_empty() {
+wuffs_base__range_ie_u32::is_empty() const {
   return wuffs_base__range_ie_u32__is_empty(this);
 }
 
 inline bool  //
-wuffs_base__range_ie_u32::equals(wuffs_base__range_ie_u32 s) {
+wuffs_base__range_ie_u32::equals(wuffs_base__range_ie_u32 s) const {
   return wuffs_base__range_ie_u32__equals(this, s);
 }
 
 inline wuffs_base__range_ie_u32  //
-wuffs_base__range_ie_u32::intersect(wuffs_base__range_ie_u32 s) {
+wuffs_base__range_ie_u32::intersect(wuffs_base__range_ie_u32 s) const {
   return wuffs_base__range_ie_u32__intersect(this, s);
 }
 
 inline wuffs_base__range_ie_u32  //
-wuffs_base__range_ie_u32::unite(wuffs_base__range_ie_u32 s) {
+wuffs_base__range_ie_u32::unite(wuffs_base__range_ie_u32 s) const {
   return wuffs_base__range_ie_u32__unite(this, s);
 }
 
 inline bool  //
-wuffs_base__range_ie_u32::contains(uint32_t x) {
+wuffs_base__range_ie_u32::contains(uint32_t x) const {
   return wuffs_base__range_ie_u32__contains(this, x);
 }
 
 inline bool  //
-wuffs_base__range_ie_u32::contains_range(wuffs_base__range_ie_u32 s) {
+wuffs_base__range_ie_u32::contains_range(wuffs_base__range_ie_u32 s) const {
   return wuffs_base__range_ie_u32__contains_range(this, s);
 }
 
 inline uint32_t  //
-wuffs_base__range_ie_u32::length() {
+wuffs_base__range_ie_u32::length() const {
   return wuffs_base__range_ie_u32__length(this);
 }
 
@@ -283,14 +285,14 @@
   uint64_t max_incl;
 
 #ifdef __cplusplus
-  inline bool is_empty();
-  inline bool equals(wuffs_base__range_ii_u64__struct s);
+  inline bool is_empty() const;
+  inline bool equals(wuffs_base__range_ii_u64__struct s) const;
   inline wuffs_base__range_ii_u64__struct intersect(
-      wuffs_base__range_ii_u64__struct s);
+      wuffs_base__range_ii_u64__struct s) const;
   inline wuffs_base__range_ii_u64__struct unite(
-      wuffs_base__range_ii_u64__struct s);
-  inline bool contains(uint64_t x);
-  inline bool contains_range(wuffs_base__range_ii_u64__struct s);
+      wuffs_base__range_ii_u64__struct s) const;
+  inline bool contains(uint64_t x) const;
+  inline bool contains_range(wuffs_base__range_ii_u64__struct s) const;
 #endif  // __cplusplus
 
 } wuffs_base__range_ii_u64;
@@ -304,12 +306,12 @@
 }
 
 static inline bool  //
-wuffs_base__range_ii_u64__is_empty(wuffs_base__range_ii_u64* r) {
+wuffs_base__range_ii_u64__is_empty(const wuffs_base__range_ii_u64* r) {
   return r->min_incl > r->max_incl;
 }
 
 static inline bool  //
-wuffs_base__range_ii_u64__equals(wuffs_base__range_ii_u64* r,
+wuffs_base__range_ii_u64__equals(const wuffs_base__range_ii_u64* r,
                                  wuffs_base__range_ii_u64 s) {
   return (r->min_incl == s.min_incl && r->max_incl == s.max_incl) ||
          (wuffs_base__range_ii_u64__is_empty(r) &&
@@ -317,7 +319,7 @@
 }
 
 static inline wuffs_base__range_ii_u64  //
-wuffs_base__range_ii_u64__intersect(wuffs_base__range_ii_u64* r,
+wuffs_base__range_ii_u64__intersect(const wuffs_base__range_ii_u64* r,
                                     wuffs_base__range_ii_u64 s) {
   wuffs_base__range_ii_u64 t;
   t.min_incl = wuffs_base__u64__max(r->min_incl, s.min_incl);
@@ -326,7 +328,7 @@
 }
 
 static inline wuffs_base__range_ii_u64  //
-wuffs_base__range_ii_u64__unite(wuffs_base__range_ii_u64* r,
+wuffs_base__range_ii_u64__unite(const wuffs_base__range_ii_u64* r,
                                 wuffs_base__range_ii_u64 s) {
   if (wuffs_base__range_ii_u64__is_empty(r)) {
     return s;
@@ -341,12 +343,13 @@
 }
 
 static inline bool  //
-wuffs_base__range_ii_u64__contains(wuffs_base__range_ii_u64* r, uint64_t x) {
+wuffs_base__range_ii_u64__contains(const wuffs_base__range_ii_u64* r,
+                                   uint64_t x) {
   return (r->min_incl <= x) && (x <= r->max_incl);
 }
 
 static inline bool  //
-wuffs_base__range_ii_u64__contains_range(wuffs_base__range_ii_u64* r,
+wuffs_base__range_ii_u64__contains_range(const wuffs_base__range_ii_u64* r,
                                          wuffs_base__range_ii_u64 s) {
   return wuffs_base__range_ii_u64__equals(
       &s, wuffs_base__range_ii_u64__intersect(r, s));
@@ -355,32 +358,32 @@
 #ifdef __cplusplus
 
 inline bool  //
-wuffs_base__range_ii_u64::is_empty() {
+wuffs_base__range_ii_u64::is_empty() const {
   return wuffs_base__range_ii_u64__is_empty(this);
 }
 
 inline bool  //
-wuffs_base__range_ii_u64::equals(wuffs_base__range_ii_u64 s) {
+wuffs_base__range_ii_u64::equals(wuffs_base__range_ii_u64 s) const {
   return wuffs_base__range_ii_u64__equals(this, s);
 }
 
 inline wuffs_base__range_ii_u64  //
-wuffs_base__range_ii_u64::intersect(wuffs_base__range_ii_u64 s) {
+wuffs_base__range_ii_u64::intersect(wuffs_base__range_ii_u64 s) const {
   return wuffs_base__range_ii_u64__intersect(this, s);
 }
 
 inline wuffs_base__range_ii_u64  //
-wuffs_base__range_ii_u64::unite(wuffs_base__range_ii_u64 s) {
+wuffs_base__range_ii_u64::unite(wuffs_base__range_ii_u64 s) const {
   return wuffs_base__range_ii_u64__unite(this, s);
 }
 
 inline bool  //
-wuffs_base__range_ii_u64::contains(uint64_t x) {
+wuffs_base__range_ii_u64::contains(uint64_t x) const {
   return wuffs_base__range_ii_u64__contains(this, x);
 }
 
 inline bool  //
-wuffs_base__range_ii_u64::contains_range(wuffs_base__range_ii_u64 s) {
+wuffs_base__range_ii_u64::contains_range(wuffs_base__range_ii_u64 s) const {
   return wuffs_base__range_ii_u64__contains_range(this, s);
 }
 
@@ -393,15 +396,15 @@
   uint64_t max_excl;
 
 #ifdef __cplusplus
-  inline bool is_empty();
-  inline bool equals(wuffs_base__range_ie_u64__struct s);
+  inline bool is_empty() const;
+  inline bool equals(wuffs_base__range_ie_u64__struct s) const;
   inline wuffs_base__range_ie_u64__struct intersect(
-      wuffs_base__range_ie_u64__struct s);
+      wuffs_base__range_ie_u64__struct s) const;
   inline wuffs_base__range_ie_u64__struct unite(
-      wuffs_base__range_ie_u64__struct s);
-  inline bool contains(uint64_t x);
-  inline bool contains_range(wuffs_base__range_ie_u64__struct s);
-  inline uint64_t length();
+      wuffs_base__range_ie_u64__struct s) const;
+  inline bool contains(uint64_t x) const;
+  inline bool contains_range(wuffs_base__range_ie_u64__struct s) const;
+  inline uint64_t length() const;
 #endif  // __cplusplus
 
 } wuffs_base__range_ie_u64;
@@ -415,12 +418,12 @@
 }
 
 static inline bool  //
-wuffs_base__range_ie_u64__is_empty(wuffs_base__range_ie_u64* r) {
+wuffs_base__range_ie_u64__is_empty(const wuffs_base__range_ie_u64* r) {
   return r->min_incl >= r->max_excl;
 }
 
 static inline bool  //
-wuffs_base__range_ie_u64__equals(wuffs_base__range_ie_u64* r,
+wuffs_base__range_ie_u64__equals(const wuffs_base__range_ie_u64* r,
                                  wuffs_base__range_ie_u64 s) {
   return (r->min_incl == s.min_incl && r->max_excl == s.max_excl) ||
          (wuffs_base__range_ie_u64__is_empty(r) &&
@@ -428,7 +431,7 @@
 }
 
 static inline wuffs_base__range_ie_u64  //
-wuffs_base__range_ie_u64__intersect(wuffs_base__range_ie_u64* r,
+wuffs_base__range_ie_u64__intersect(const wuffs_base__range_ie_u64* r,
                                     wuffs_base__range_ie_u64 s) {
   wuffs_base__range_ie_u64 t;
   t.min_incl = wuffs_base__u64__max(r->min_incl, s.min_incl);
@@ -437,7 +440,7 @@
 }
 
 static inline wuffs_base__range_ie_u64  //
-wuffs_base__range_ie_u64__unite(wuffs_base__range_ie_u64* r,
+wuffs_base__range_ie_u64__unite(const wuffs_base__range_ie_u64* r,
                                 wuffs_base__range_ie_u64 s) {
   if (wuffs_base__range_ie_u64__is_empty(r)) {
     return s;
@@ -452,56 +455,57 @@
 }
 
 static inline bool  //
-wuffs_base__range_ie_u64__contains(wuffs_base__range_ie_u64* r, uint64_t x) {
+wuffs_base__range_ie_u64__contains(const wuffs_base__range_ie_u64* r,
+                                   uint64_t x) {
   return (r->min_incl <= x) && (x < r->max_excl);
 }
 
 static inline bool  //
-wuffs_base__range_ie_u64__contains_range(wuffs_base__range_ie_u64* r,
+wuffs_base__range_ie_u64__contains_range(const wuffs_base__range_ie_u64* r,
                                          wuffs_base__range_ie_u64 s) {
   return wuffs_base__range_ie_u64__equals(
       &s, wuffs_base__range_ie_u64__intersect(r, s));
 }
 
 static inline uint64_t  //
-wuffs_base__range_ie_u64__length(wuffs_base__range_ie_u64* r) {
+wuffs_base__range_ie_u64__length(const wuffs_base__range_ie_u64* r) {
   return wuffs_base__u64__sat_sub(r->max_excl, r->min_incl);
 }
 
 #ifdef __cplusplus
 
 inline bool  //
-wuffs_base__range_ie_u64::is_empty() {
+wuffs_base__range_ie_u64::is_empty() const {
   return wuffs_base__range_ie_u64__is_empty(this);
 }
 
 inline bool  //
-wuffs_base__range_ie_u64::equals(wuffs_base__range_ie_u64 s) {
+wuffs_base__range_ie_u64::equals(wuffs_base__range_ie_u64 s) const {
   return wuffs_base__range_ie_u64__equals(this, s);
 }
 
 inline wuffs_base__range_ie_u64  //
-wuffs_base__range_ie_u64::intersect(wuffs_base__range_ie_u64 s) {
+wuffs_base__range_ie_u64::intersect(wuffs_base__range_ie_u64 s) const {
   return wuffs_base__range_ie_u64__intersect(this, s);
 }
 
 inline wuffs_base__range_ie_u64  //
-wuffs_base__range_ie_u64::unite(wuffs_base__range_ie_u64 s) {
+wuffs_base__range_ie_u64::unite(wuffs_base__range_ie_u64 s) const {
   return wuffs_base__range_ie_u64__unite(this, s);
 }
 
 inline bool  //
-wuffs_base__range_ie_u64::contains(uint64_t x) {
+wuffs_base__range_ie_u64::contains(uint64_t x) const {
   return wuffs_base__range_ie_u64__contains(this, x);
 }
 
 inline bool  //
-wuffs_base__range_ie_u64::contains_range(wuffs_base__range_ie_u64 s) {
+wuffs_base__range_ie_u64::contains_range(wuffs_base__range_ie_u64 s) const {
   return wuffs_base__range_ie_u64__contains_range(this, s);
 }
 
 inline uint64_t  //
-wuffs_base__range_ie_u64::length() {
+wuffs_base__range_ie_u64::length() const {
   return wuffs_base__range_ie_u64__length(this);
 }
 
@@ -525,14 +529,14 @@
   uint32_t max_incl_y;
 
 #ifdef __cplusplus
-  inline bool is_empty();
-  inline bool equals(wuffs_base__rect_ii_u32__struct s);
+  inline bool is_empty() const;
+  inline bool equals(wuffs_base__rect_ii_u32__struct s) const;
   inline wuffs_base__rect_ii_u32__struct intersect(
-      wuffs_base__rect_ii_u32__struct s);
+      wuffs_base__rect_ii_u32__struct s) const;
   inline wuffs_base__rect_ii_u32__struct unite(
-      wuffs_base__rect_ii_u32__struct s);
-  inline bool contains(uint32_t x, uint32_t y);
-  inline bool contains_rect(wuffs_base__rect_ii_u32__struct s);
+      wuffs_base__rect_ii_u32__struct s) const;
+  inline bool contains(uint32_t x, uint32_t y) const;
+  inline bool contains_rect(wuffs_base__rect_ii_u32__struct s) const;
 #endif  // __cplusplus
 
 } wuffs_base__rect_ii_u32;
@@ -551,12 +555,12 @@
 }
 
 static inline bool  //
-wuffs_base__rect_ii_u32__is_empty(wuffs_base__rect_ii_u32* r) {
+wuffs_base__rect_ii_u32__is_empty(const wuffs_base__rect_ii_u32* r) {
   return (r->min_incl_x > r->max_incl_x) || (r->min_incl_y > r->max_incl_y);
 }
 
 static inline bool  //
-wuffs_base__rect_ii_u32__equals(wuffs_base__rect_ii_u32* r,
+wuffs_base__rect_ii_u32__equals(const wuffs_base__rect_ii_u32* r,
                                 wuffs_base__rect_ii_u32 s) {
   return (r->min_incl_x == s.min_incl_x && r->min_incl_y == s.min_incl_y &&
           r->max_incl_x == s.max_incl_x && r->max_incl_y == s.max_incl_y) ||
@@ -565,7 +569,7 @@
 }
 
 static inline wuffs_base__rect_ii_u32  //
-wuffs_base__rect_ii_u32__intersect(wuffs_base__rect_ii_u32* r,
+wuffs_base__rect_ii_u32__intersect(const wuffs_base__rect_ii_u32* r,
                                    wuffs_base__rect_ii_u32 s) {
   wuffs_base__rect_ii_u32 t;
   t.min_incl_x = wuffs_base__u32__max(r->min_incl_x, s.min_incl_x);
@@ -576,7 +580,7 @@
 }
 
 static inline wuffs_base__rect_ii_u32  //
-wuffs_base__rect_ii_u32__unite(wuffs_base__rect_ii_u32* r,
+wuffs_base__rect_ii_u32__unite(const wuffs_base__rect_ii_u32* r,
                                wuffs_base__rect_ii_u32 s) {
   if (wuffs_base__rect_ii_u32__is_empty(r)) {
     return s;
@@ -593,7 +597,7 @@
 }
 
 static inline bool  //
-wuffs_base__rect_ii_u32__contains(wuffs_base__rect_ii_u32* r,
+wuffs_base__rect_ii_u32__contains(const wuffs_base__rect_ii_u32* r,
                                   uint32_t x,
                                   uint32_t y) {
   return (r->min_incl_x <= x) && (x <= r->max_incl_x) && (r->min_incl_y <= y) &&
@@ -601,7 +605,7 @@
 }
 
 static inline bool  //
-wuffs_base__rect_ii_u32__contains_rect(wuffs_base__rect_ii_u32* r,
+wuffs_base__rect_ii_u32__contains_rect(const wuffs_base__rect_ii_u32* r,
                                        wuffs_base__rect_ii_u32 s) {
   return wuffs_base__rect_ii_u32__equals(
       &s, wuffs_base__rect_ii_u32__intersect(r, s));
@@ -610,32 +614,32 @@
 #ifdef __cplusplus
 
 inline bool  //
-wuffs_base__rect_ii_u32::is_empty() {
+wuffs_base__rect_ii_u32::is_empty() const {
   return wuffs_base__rect_ii_u32__is_empty(this);
 }
 
 inline bool  //
-wuffs_base__rect_ii_u32::equals(wuffs_base__rect_ii_u32 s) {
+wuffs_base__rect_ii_u32::equals(wuffs_base__rect_ii_u32 s) const {
   return wuffs_base__rect_ii_u32__equals(this, s);
 }
 
 inline wuffs_base__rect_ii_u32  //
-wuffs_base__rect_ii_u32::intersect(wuffs_base__rect_ii_u32 s) {
+wuffs_base__rect_ii_u32::intersect(wuffs_base__rect_ii_u32 s) const {
   return wuffs_base__rect_ii_u32__intersect(this, s);
 }
 
 inline wuffs_base__rect_ii_u32  //
-wuffs_base__rect_ii_u32::unite(wuffs_base__rect_ii_u32 s) {
+wuffs_base__rect_ii_u32::unite(wuffs_base__rect_ii_u32 s) const {
   return wuffs_base__rect_ii_u32__unite(this, s);
 }
 
 inline bool  //
-wuffs_base__rect_ii_u32::contains(uint32_t x, uint32_t y) {
+wuffs_base__rect_ii_u32::contains(uint32_t x, uint32_t y) const {
   return wuffs_base__rect_ii_u32__contains(this, x, y);
 }
 
 inline bool  //
-wuffs_base__rect_ii_u32::contains_rect(wuffs_base__rect_ii_u32 s) {
+wuffs_base__rect_ii_u32::contains_rect(wuffs_base__rect_ii_u32 s) const {
   return wuffs_base__rect_ii_u32__contains_rect(this, s);
 }
 
@@ -660,16 +664,16 @@
   uint32_t max_excl_y;
 
 #ifdef __cplusplus
-  inline bool is_empty();
-  inline bool equals(wuffs_base__rect_ie_u32__struct s);
+  inline bool is_empty() const;
+  inline bool equals(wuffs_base__rect_ie_u32__struct s) const;
   inline wuffs_base__rect_ie_u32__struct intersect(
-      wuffs_base__rect_ie_u32__struct s);
+      wuffs_base__rect_ie_u32__struct s) const;
   inline wuffs_base__rect_ie_u32__struct unite(
-      wuffs_base__rect_ie_u32__struct s);
-  inline bool contains(uint32_t x, uint32_t y);
-  inline bool contains_rect(wuffs_base__rect_ie_u32__struct s);
-  inline uint32_t width();
-  inline uint32_t height();
+      wuffs_base__rect_ie_u32__struct s) const;
+  inline bool contains(uint32_t x, uint32_t y) const;
+  inline bool contains_rect(wuffs_base__rect_ie_u32__struct s) const;
+  inline uint32_t width() const;
+  inline uint32_t height() const;
 #endif  // __cplusplus
 
 } wuffs_base__rect_ie_u32;
@@ -688,12 +692,12 @@
 }
 
 static inline bool  //
-wuffs_base__rect_ie_u32__is_empty(wuffs_base__rect_ie_u32* r) {
+wuffs_base__rect_ie_u32__is_empty(const wuffs_base__rect_ie_u32* r) {
   return (r->min_incl_x >= r->max_excl_x) || (r->min_incl_y >= r->max_excl_y);
 }
 
 static inline bool  //
-wuffs_base__rect_ie_u32__equals(wuffs_base__rect_ie_u32* r,
+wuffs_base__rect_ie_u32__equals(const wuffs_base__rect_ie_u32* r,
                                 wuffs_base__rect_ie_u32 s) {
   return (r->min_incl_x == s.min_incl_x && r->min_incl_y == s.min_incl_y &&
           r->max_excl_x == s.max_excl_x && r->max_excl_y == s.max_excl_y) ||
@@ -702,7 +706,7 @@
 }
 
 static inline wuffs_base__rect_ie_u32  //
-wuffs_base__rect_ie_u32__intersect(wuffs_base__rect_ie_u32* r,
+wuffs_base__rect_ie_u32__intersect(const wuffs_base__rect_ie_u32* r,
                                    wuffs_base__rect_ie_u32 s) {
   wuffs_base__rect_ie_u32 t;
   t.min_incl_x = wuffs_base__u32__max(r->min_incl_x, s.min_incl_x);
@@ -713,7 +717,7 @@
 }
 
 static inline wuffs_base__rect_ie_u32  //
-wuffs_base__rect_ie_u32__unite(wuffs_base__rect_ie_u32* r,
+wuffs_base__rect_ie_u32__unite(const wuffs_base__rect_ie_u32* r,
                                wuffs_base__rect_ie_u32 s) {
   if (wuffs_base__rect_ie_u32__is_empty(r)) {
     return s;
@@ -730,7 +734,7 @@
 }
 
 static inline bool  //
-wuffs_base__rect_ie_u32__contains(wuffs_base__rect_ie_u32* r,
+wuffs_base__rect_ie_u32__contains(const wuffs_base__rect_ie_u32* r,
                                   uint32_t x,
                                   uint32_t y) {
   return (r->min_incl_x <= x) && (x < r->max_excl_x) && (r->min_incl_y <= y) &&
@@ -738,61 +742,61 @@
 }
 
 static inline bool  //
-wuffs_base__rect_ie_u32__contains_rect(wuffs_base__rect_ie_u32* r,
+wuffs_base__rect_ie_u32__contains_rect(const wuffs_base__rect_ie_u32* r,
                                        wuffs_base__rect_ie_u32 s) {
   return wuffs_base__rect_ie_u32__equals(
       &s, wuffs_base__rect_ie_u32__intersect(r, s));
 }
 
 static inline uint32_t  //
-wuffs_base__rect_ie_u32__width(wuffs_base__rect_ie_u32* r) {
+wuffs_base__rect_ie_u32__width(const wuffs_base__rect_ie_u32* r) {
   return wuffs_base__u32__sat_sub(r->max_excl_x, r->min_incl_x);
 }
 
 static inline uint32_t  //
-wuffs_base__rect_ie_u32__height(wuffs_base__rect_ie_u32* r) {
+wuffs_base__rect_ie_u32__height(const wuffs_base__rect_ie_u32* r) {
   return wuffs_base__u32__sat_sub(r->max_excl_y, r->min_incl_y);
 }
 
 #ifdef __cplusplus
 
 inline bool  //
-wuffs_base__rect_ie_u32::is_empty() {
+wuffs_base__rect_ie_u32::is_empty() const {
   return wuffs_base__rect_ie_u32__is_empty(this);
 }
 
 inline bool  //
-wuffs_base__rect_ie_u32::equals(wuffs_base__rect_ie_u32 s) {
+wuffs_base__rect_ie_u32::equals(wuffs_base__rect_ie_u32 s) const {
   return wuffs_base__rect_ie_u32__equals(this, s);
 }
 
 inline wuffs_base__rect_ie_u32  //
-wuffs_base__rect_ie_u32::intersect(wuffs_base__rect_ie_u32 s) {
+wuffs_base__rect_ie_u32::intersect(wuffs_base__rect_ie_u32 s) const {
   return wuffs_base__rect_ie_u32__intersect(this, s);
 }
 
 inline wuffs_base__rect_ie_u32  //
-wuffs_base__rect_ie_u32::unite(wuffs_base__rect_ie_u32 s) {
+wuffs_base__rect_ie_u32::unite(wuffs_base__rect_ie_u32 s) const {
   return wuffs_base__rect_ie_u32__unite(this, s);
 }
 
 inline bool  //
-wuffs_base__rect_ie_u32::contains(uint32_t x, uint32_t y) {
+wuffs_base__rect_ie_u32::contains(uint32_t x, uint32_t y) const {
   return wuffs_base__rect_ie_u32__contains(this, x, y);
 }
 
 inline bool  //
-wuffs_base__rect_ie_u32::contains_rect(wuffs_base__rect_ie_u32 s) {
+wuffs_base__rect_ie_u32::contains_rect(wuffs_base__rect_ie_u32 s) const {
   return wuffs_base__rect_ie_u32__contains_rect(this, s);
 }
 
 inline uint32_t  //
-wuffs_base__rect_ie_u32::width() {
+wuffs_base__rect_ie_u32::width() const {
   return wuffs_base__rect_ie_u32__width(this);
 }
 
 inline uint32_t  //
-wuffs_base__rect_ie_u32::height() {
+wuffs_base__rect_ie_u32::height() const {
   return wuffs_base__rect_ie_u32__height(this);
 }
 
diff --git a/internal/cgen/builtin.go b/internal/cgen/builtin.go
index 636637e..1529d6b 100644
--- a/internal/cgen/builtin.go
+++ b/internal/cgen/builtin.go
@@ -102,75 +102,68 @@
 func (g *gen) writeBuiltinIO(b *buffer, recv *a.Expr, method t.ID, args []*a.Node, depth uint32) error {
 	switch method {
 	case t.IDAvailable:
-		p0, p1 := "", ""
+		p, q := "", ""
 		// TODO: don't hard-code these.
 		switch recv.Str(g.tm) {
 		case "args.dst":
-			p0 = "io1_a_dst"
-			p1 = "iop_a_dst"
+			p = "io2_a_dst"
+			q = "iop_a_dst"
 		case "args.src":
-			p0 = "io1_a_src"
-			p1 = "iop_a_src"
+			p = "io2_a_src"
+			q = "iop_a_src"
 		case "w":
-			p0 = "io1_v_w"
-			p1 = "iop_v_w"
+			p = "io2_v_w"
+			q = "iop_v_w"
 		}
-		if p0 == "" {
+		if p == "" {
 			return fmt.Errorf(`TODO: cgen a "foo.available" expression`)
 		}
-		b.printf("((uint64_t)(%s - %s))", p0, p1)
+		b.printf("((uint64_t)(%s - %s))", p, q)
 		return nil
-
-	case t.IDSet:
-		typ, v := "reader", "r"
-		if len(args) == 1 {
-			typ, v = "writer", "w"
-		}
-		b.printf("wuffs_base__io_%s__set(&v_%s, &u_%s, &iop_v_%s, &io1_v_%s,", typ, v, v, v, v)
-		return g.writeArgs(b, args, depth)
-
 	}
 	return errNoSuchBuiltin
 }
 
 func (g *gen) writeBuiltinIOReader(b *buffer, recv *a.Expr, method t.ID, args []*a.Node, depth uint32) error {
 	// TODO: don't hard-code the recv being a_src.
+	prefix, name := aPrefix, "src"
+	if recv.Operator() == 0 {
+		prefix, name = vPrefix, recv.Ident().Str(g.tm)
+	}
+
 	switch method {
 	case t.IDUndoByte:
 		b.writes("(iop_a_src--, wuffs_base__make_empty_struct())")
 		return nil
 
 	case t.IDCanUndoByte:
-		b.writes("(iop_a_src > io0_a_src)")
+		b.writes("(iop_a_src > io1_a_src)")
+		return nil
+
+	case t.IDCountSince:
+		b.printf("wuffs_base__io__count_since(")
+		if err := g.writeExpr(b, args[0].AsArg().Value(), depth); err != nil {
+			return err
+		}
+		b.printf(", ((uint64_t)(%s%s%s - %s%s%s)))", iopPrefix, prefix, name, io0Prefix, prefix, name)
+		return nil
+
+	case t.IDMark:
+		b.printf("((uint64_t)(%s%s%s - %s%s%s))", iopPrefix, prefix, name, io0Prefix, prefix, name)
 		return nil
 
 	case t.IDPosition:
-		b.printf("(a_src.private_impl.buf ? wuffs_base__u64__sat_add(" +
-			"a_src.private_impl.buf->meta.pos, ((uint64_t)(iop_a_src - a_src.private_impl.buf->data.ptr))) : 0)")
+		b.printf("(a_src ? wuffs_base__u64__sat_add(" +
+			"a_src->meta.pos, ((uint64_t)(iop_a_src - a_src->data.ptr))) : 0)")
 		return nil
 
-	case t.IDSetLimit:
-		b.printf("wuffs_base__io_reader__set_limit(&%ssrc, iop_a_src,", aPrefix)
-		// TODO: update the iop variables?
-		return g.writeArgs(b, args, depth)
-
-	case t.IDSetMark:
-		b.printf("wuffs_base__io_reader__set_mark(&%ssrc, iop_a_src)", aPrefix)
-		return nil
-
-	case t.IDSinceMark, t.IDSinceMarkLength:
-		prefix, name := aPrefix, "src"
-		if recv.Operator() == 0 {
-			prefix, name = vPrefix, recv.Ident().Str(g.tm)
+	case t.IDSince:
+		b.printf("wuffs_base__io__since(")
+		if err := g.writeExpr(b, args[0].AsArg().Value(), depth); err != nil {
+			return err
 		}
-
-		if method == t.IDSinceMark {
-			b.printf("wuffs_base__make_slice_u8(%s%s.private_impl.mark, (size_t)(", prefix, name)
-		}
-		b.printf("iop_%s%s - %s%s.private_impl.mark", prefix, name, prefix, name)
-		if method == t.IDSinceMark {
-			b.writes("))")
-		}
+		b.printf(", ((uint64_t)(%s%s%s - %s%s%s)), %s%s%s)",
+			iopPrefix, prefix, name, io0Prefix, prefix, name, io0Prefix, prefix, name)
 		return nil
 
 	case t.IDSkipFast:
@@ -186,7 +179,7 @@
 		return nil
 
 	case t.IDTake:
-		b.printf("wuffs_base__io_reader__take(&iop_a_src, io1_a_src,")
+		b.printf("wuffs_base__io_reader__take(&iop_a_src, io2_a_src,")
 		return g.writeArgs(b, args, depth)
 	}
 
@@ -210,6 +203,11 @@
 
 func (g *gen) writeBuiltinIOWriter(b *buffer, recv *a.Expr, method t.ID, args []*a.Node, depth uint32) error {
 	// TODO: don't hard-code the recv being a_dst or w.
+	prefix, name := aPrefix, "dst"
+	if recv.Operator() == 0 {
+		prefix, name = vPrefix, recv.Ident().Str(g.tm)
+	}
+
 	switch method {
 	case t.IDCopyNFromHistory, t.IDCopyNFromHistoryFast:
 		suffix := ""
@@ -217,7 +215,7 @@
 			suffix = "_fast"
 		}
 		b.printf("wuffs_base__io_writer__copy_n_from_history%s("+
-			"&iop_a_dst, %sdst.private_impl.mark, io1_a_dst",
+			"&iop_a_dst, %sdst->data.ptr, io2_a_dst",
 			suffix, aPrefix)
 		for _, o := range args {
 			b.writeb(',')
@@ -229,48 +227,48 @@
 		return nil
 
 	case t.IDCopyNFromReader:
-		b.printf("wuffs_base__io_writer__copy_n_from_reader(&iop_a_dst, io1_a_dst,")
+		b.printf("wuffs_base__io_writer__copy_n_from_reader(&iop_a_dst, io2_a_dst,")
 		if err := g.writeExpr(b, args[0].AsArg().Value(), depth); err != nil {
 			return err
 		}
 		// TODO: don't assume that the last argument is "args.src".
-		b.printf(", &iop_a_src, io1_a_src)")
+		b.printf(", &iop_a_src, io2_a_src)")
 		return nil
 
 	case t.IDCopyFromSlice:
-		b.printf("wuffs_base__io_writer__copy_from_slice(&iop_a_dst, io1_a_dst,")
+		b.printf("wuffs_base__io_writer__copy_from_slice(&iop_a_dst, io2_a_dst,")
 		return g.writeArgs(b, args, depth)
 
 	case t.IDCopyNFromSlice:
-		b.printf("wuffs_base__io_writer__copy_n_from_slice(&iop_a_dst, io1_a_dst,")
+		b.printf("wuffs_base__io_writer__copy_n_from_slice(&iop_a_dst, io2_a_dst,")
 		return g.writeArgs(b, args, depth)
 
+	case t.IDCountSince:
+		b.printf("wuffs_base__io__count_since(")
+		if err := g.writeExpr(b, args[0].AsArg().Value(), depth); err != nil {
+			return err
+		}
+		b.printf(", ((uint64_t)(iop_a_dst - io0_a_dst)))")
+		return nil
+
+	case t.IDHistoryAvailable:
+		b.printf("((uint64_t)(iop_%s%s - %s%s->data.ptr))", prefix, name, prefix, name)
+		return nil
+
+	case t.IDMark:
+		b.printf("((uint64_t)(iop_a_dst - io0_a_dst))")
+		return nil
+
 	case t.IDPosition:
-		b.printf("(a_dst.private_impl.buf ? wuffs_base__u64__sat_add(" +
-			"a_dst.private_impl.buf->meta.pos, iop_a_dst - a_dst.private_impl.buf->data.ptr) : 0)")
+		b.printf("(a_dst ? wuffs_base__u64__sat_add(a_dst->meta.pos, iop_a_dst - a_dst->data.ptr) : 0)")
 		return nil
 
-	case t.IDSetMark:
-		// TODO: is a private_impl.mark the right representation? What
-		// if the function is passed a (ptr io_writer) instead of a
-		// (io_writer)? Do we still want to have that mark live outside of
-		// the function scope?
-		b.printf("wuffs_base__io_writer__set_mark(&%sdst, iop_a_dst)", aPrefix)
-		return nil
-
-	case t.IDSinceMark, t.IDSinceMarkLength:
-		prefix, name := aPrefix, "dst"
-		if recv.Operator() == 0 {
-			prefix, name = vPrefix, recv.Ident().Str(g.tm)
+	case t.IDSince:
+		b.printf("wuffs_base__io__since(")
+		if err := g.writeExpr(b, args[0].AsArg().Value(), depth); err != nil {
+			return err
 		}
-
-		if method == t.IDSinceMark {
-			b.printf("wuffs_base__make_slice_u8(%s%s.private_impl.mark, (size_t)(", prefix, name)
-		}
-		b.printf("iop_%s%s - %s%s.private_impl.mark", prefix, name, prefix, name)
-		if method == t.IDSinceMark {
-			b.writes("))")
-		}
+		b.printf(", ((uint64_t)(iop_a_dst - io0_a_dst)), io0_a_dst)")
 		return nil
 	}
 
@@ -401,25 +399,6 @@
 		return g.writeArgs(b, args, depth)
 
 	case t.IDLength:
-		if recv.Operator() == t.IDOpenParen {
-			if method := recv.LHS().AsExpr(); method.Operator() == t.IDDot && method.Ident() == t.IDSinceMark {
-				if lhs := method.LHS().AsExpr(); lhs.MType().IsIOType() {
-					b.writes("((uint64_t)(")
-					if lhs.MType().QID()[1] == t.IDIOReader {
-						if err := g.writeBuiltinIOReader(b, lhs, t.IDSinceMarkLength, nil, depth); err != nil {
-							return err
-						}
-					} else {
-						if err := g.writeBuiltinIOWriter(b, lhs, t.IDSinceMarkLength, nil, depth); err != nil {
-							return err
-						}
-					}
-					b.writes("))")
-					return nil
-				}
-			}
-		}
-
 		b.writes("((uint64_t)(")
 		if err := g.writeExpr(b, recv, depth); err != nil {
 			return err
@@ -579,7 +558,7 @@
 			temp := g.currFunk.tempW
 			g.currFunk.tempW++
 
-			b.printf("if (WUFFS_BASE__UNLIKELY(iop_a_src == io1_a_src)) {" +
+			b.printf("if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {" +
 				"status = wuffs_base__suspension__short_read; goto suspend; }")
 
 			// TODO: watch for passing an array type to writeCTypeName? In C, an
@@ -596,7 +575,7 @@
 				if err := g.writeCoroSuspPoint(b, false); err != nil {
 					return err
 				}
-				b.printf("if (WUFFS_BASE__UNLIKELY(iop_a_src == io1_a_src)) {" +
+				b.printf("if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {" +
 					"status = wuffs_base__suspension__short_read; goto suspend; }")
 				b.printf("iop_a_src++;\n")
 				return nil
@@ -617,9 +596,9 @@
 				return err
 			}
 
-			b.printf("if (%s > ((uint64_t)(io1_a_src - iop_a_src))) {\n", scratchName)
-			b.printf("%s -= ((uint64_t)(io1_a_src - iop_a_src));\n", scratchName)
-			b.printf("iop_a_src = io1_a_src;\n")
+			b.printf("if (%s > ((uint64_t)(io2_a_src - iop_a_src))) {\n", scratchName)
+			b.printf("%s -= ((uint64_t)(io2_a_src - iop_a_src));\n", scratchName)
+			b.printf("iop_a_src = io2_a_src;\n")
 
 			b.writes("status = wuffs_base__suspension__short_read; goto suspend; }\n")
 			b.printf("iop_a_src += %s;\n", scratchName)
@@ -643,7 +622,7 @@
 			if err := g.writeCoroSuspPoint(b, false); err != nil {
 				return err
 			}
-			b.writes("if (iop_a_dst == io1_a_dst) {\n" +
+			b.writes("if (iop_a_dst == io2_a_dst) {\n" +
 				"status = wuffs_base__suspension__short_write; goto suspend; }\n" +
 				"*iop_a_dst++ = ")
 			x := n.Args()[0].AsArg().Value()
@@ -681,7 +660,7 @@
 	scratchName := fmt.Sprintf("self->private_data.%s%s[0].scratch",
 		sPrefix, g.currFunk.astFunc.FuncName().Str(g.tm))
 
-	b.printf("if (WUFFS_BASE__LIKELY(io1_a_src - iop_a_src >= %d)) {", xx/8)
+	b.printf("if (WUFFS_BASE__LIKELY(io2_a_src - iop_a_src >= %d)) {", xx/8)
 	b.printf("%s%d =", tPrefix, temp)
 	if xx != yy {
 		b.printf("((uint%d_t)(", yy)
@@ -699,7 +678,7 @@
 	}
 	b.printf("while (true) {")
 
-	b.printf("if (WUFFS_BASE__UNLIKELY(iop_%s == io1_%s)) {"+
+	b.printf("if (WUFFS_BASE__UNLIKELY(iop_%s == io2_%s)) {"+
 		"status = wuffs_base__suspension__short_read; goto suspend; }",
 		preName, preName)
 
diff --git a/internal/cgen/cgen.go b/internal/cgen/cgen.go
index 52f9b3c..d5cb53b 100644
--- a/internal/cgen/cgen.go
+++ b/internal/cgen/cgen.go
@@ -68,27 +68,29 @@
 // reading or writing the next byte (and advancing the stream) is essentially
 // "etc = *iop_a_src++" or "*io_a_dst++ = etc".
 //
-// The other two prefixes, giving names like io0_etc and io1_etc, are
+// The other two prefixes, giving names like io1_etc and io2_etc, are
 // auxilliary pointers: lower and upper inclusive bounds. As an iop_etc pointer
-// advances, it cannot advance past io1_etc. In the rarer case that an iop_etc
-// pointer retreats, undoing a read or write, it cannot retreat past io0_etc.
+// advances, it cannot advance past io2_etc. In the rarer case that an iop_etc
+// pointer retreats, undoing a read or write, it cannot retreat past io1_etc.
+//
+// The iop_etc pointer can change over the lifetime of a function. The ioN_etc
+// pointers, for numeric N, cannot.
 //
 // At the start of a function, these pointers are initialized from an
-// io_buffer's fields (ptr, ri, wi, len), or possibly a limit field. For an
-// io_reader:
-//  - io0_etc = ptr + ri
+// io_buffer's fields (ptr, ri, wi, len). For an io_reader:
+//  - io0_etc = ptr
+//  - io1_etc = ptr + ri
 //  - iop_etc = ptr + ri
-//  - io1_etc = ptr + wi   or  limit
+//  - io2_etc = ptr + wi
 // and for an io_writer:
-//  - io0_etc = ptr + wi
+//  - io0_etc = ptr
+//  - io1_etc = ptr + wi
 //  - iop_etc = ptr + wi
-//  - io1_etc = ptr + len  or  limit
-//
-// TODO: discuss marks and limits, and how (if at all) auxilliary pointers can
-// change over a function's lifetime.
+//  - io2_etc = ptr + len
 const (
-	io0Prefix = "io0_" // Lower bound.
-	io1Prefix = "io1_" // Upper bound.
+	io0Prefix = "io0_" // Base.
+	io1Prefix = "io1_" // Lower bound.
+	io2Prefix = "io2_" // Upper bound.
 	iopPrefix = "iop_" // Pointer.
 )
 
@@ -263,6 +265,19 @@
 
 func insertBaseAllPublicH(buf *buffer) error {
 	if err := expandBangBangInsert(buf, baseCorePublicH, map[string]func(*buffer) error{
+		"// !! INSERT FourCCs.\n": func(b *buffer) error {
+			for _, z := range builtin.FourCCs {
+				b.printf("// %s.\n#define WUFFS_BASE__FOURCC__%s 0x%02X%02X%02X%02X\n\n",
+					z[1],
+					strings.ToUpper(strings.TrimSpace(z[0])),
+					z[0][0],
+					z[0][1],
+					z[0][2],
+					z[0][3],
+				)
+			}
+			return nil
+		},
 		"// !! INSERT wuffs_base__status names.\n": func(b *buffer) error {
 			for _, z := range builtin.Statuses {
 				msg, _ := t.Unescape(z)
diff --git a/internal/cgen/data.go b/internal/cgen/data.go
index 9b91b5e..09121da 100644
--- a/internal/cgen/data.go
+++ b/internal/cgen/data.go
@@ -24,11 +24,13 @@
 	""
 
 const baseImageImplC = "" +
-	"// ---------------- Images\n\nconst uint32_t wuffs_base__pixel_format__bits_per_channel[16] = {\n    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,\n    0x08, 0x0A, 0x0C, 0x10, 0x18, 0x20, 0x30, 0x40,\n};\n\nstatic uint64_t  //\nwuffs_base__pixel_swizzler__copy_1_1(wuffs_base__slice_u8 dst,\n                                     wuffs_base__slice_u8 dst_palette,\n                                     wuffs_base__slice_u8 src) {\n  return wuffs_base__slice_u8__copy_from_slice(dst, src);\n}\n\nstatic uint64_t  //\nwuffs_base__pixel_swizzler__copy_4_1(wuffs_base__slice_u8 dst,\n                                     wuffs_base__slice_u8 dst_palette,\n                                     wuffs_base__slice_u8 src) {\n  if (dst_palette.len != 1024) {\n    return 0;\n  }\n  size_t dst_len4 = dst.len / 4;\n  size_t len = dst_len4 < src.len ? dst_len4 : src.len;\n  uint8_t* d = dst.ptr;\n  uint8_t* s = src.ptr;\n\n  size_t n = len;\n  const int N = 4;\n\n  while (n >= N) {\n    wuffs_base__store_u32le(\n        d + (0 * 4),\n        wuffs_base__load_u3" +
-	"2le(dst_palette.ptr + ((uint32_t)(s[0]) * 4)));\n    wuffs_base__store_u32le(\n        d + (1 * 4),\n        wuffs_base__load_u32le(dst_palette.ptr + ((uint32_t)(s[1]) * 4)));\n    wuffs_base__store_u32le(\n        d + (2 * 4),\n        wuffs_base__load_u32le(dst_palette.ptr + ((uint32_t)(s[2]) * 4)));\n    wuffs_base__store_u32le(\n        d + (3 * 4),\n        wuffs_base__load_u32le(dst_palette.ptr + ((uint32_t)(s[3]) * 4)));\n\n    s += 1 * N;\n    d += 4 * N;\n    n -= (size_t)(1 * N);\n  }\n\n  while (n >= 1) {\n    wuffs_base__store_u32le(\n        d + (0 * 4),\n        wuffs_base__load_u32le(dst_palette.ptr + ((uint32_t)(s[0]) * 4)));\n\n    s += 1 * 1;\n    d += 4 * 1;\n    n -= (size_t)(1 * 1);\n  }\n\n  return len;\n}\n\nstatic uint64_t  //\nwuffs_base__pixel_swizzler__swap_rgbx_bgrx(wuffs_base__slice_u8 dst,\n                                           wuffs_base__slice_u8 src) {\n  size_t len4 = (dst.len < src.len ? dst.len : src.len) / 4;\n  uint8_t* d = dst.ptr;\n  uint8_t* s = src.ptr;\n\n  size_t n = len4;\n  while (n--) {\n    uin" +
-	"t8_t b0 = s[0];\n    uint8_t b1 = s[1];\n    uint8_t b2 = s[2];\n    uint8_t b3 = s[3];\n    d[0] = b2;\n    d[1] = b1;\n    d[2] = b0;\n    d[3] = b3;\n    s += 4;\n    d += 4;\n  }\n  return len4 * 4;\n}\n\nvoid  //\nwuffs_base__pixel_swizzler__prepare(wuffs_base__pixel_swizzler* p,\n                                    wuffs_base__pixel_format dst_format,\n                                    wuffs_base__slice_u8 dst_palette,\n                                    wuffs_base__pixel_format src_format,\n                                    wuffs_base__slice_u8 src_palette) {\n  if (!p) {\n    return;\n  }\n\n  // TODO: support many more formats.\n\n  uint64_t (*func)(wuffs_base__slice_u8 dst, wuffs_base__slice_u8 dst_palette,\n                   wuffs_base__slice_u8 src) = NULL;\n\n  switch (src_format) {\n    case WUFFS_BASE__PIXEL_FORMAT__INDEXED__BGRA_BINARY:\n      switch (dst_format) {\n        case WUFFS_BASE__PIXEL_FORMAT__INDEXED__BGRA_NONPREMUL:\n        case WUFFS_BASE__PIXEL_FORMAT__INDEXED__BGRA_PREMUL:\n        case WUFFS_BASE__PIXEL" +
-	"_FORMAT__INDEXED__BGRA_BINARY:\n          if (wuffs_base__slice_u8__copy_from_slice(dst_palette, src_palette) !=\n              1024) {\n            break;\n          }\n          func = wuffs_base__pixel_swizzler__copy_1_1;\n          break;\n        case WUFFS_BASE__PIXEL_FORMAT__BGRA_NONPREMUL:\n        case WUFFS_BASE__PIXEL_FORMAT__BGRA_PREMUL:\n        case WUFFS_BASE__PIXEL_FORMAT__BGRA_BINARY:\n          if (wuffs_base__slice_u8__copy_from_slice(dst_palette, src_palette) !=\n              1024) {\n            break;\n          }\n          func = wuffs_base__pixel_swizzler__copy_4_1;\n          break;\n        case WUFFS_BASE__PIXEL_FORMAT__RGBA_NONPREMUL:\n        case WUFFS_BASE__PIXEL_FORMAT__RGBA_PREMUL:\n        case WUFFS_BASE__PIXEL_FORMAT__RGBA_BINARY:\n          if (wuffs_base__pixel_swizzler__swap_rgbx_bgrx(dst_palette,\n                                                         src_palette) != 1024) {\n            break;\n          }\n          func = wuffs_base__pixel_swizzler__copy_4_1;\n          break;\n        d" +
-	"efault:\n          break;\n      }\n      break;\n\n    default:\n      break;\n  }\n\n  p->private_impl.func = func;\n}\n\nuint64_t  //\nwuffs_base__pixel_swizzler__swizzle_packed(wuffs_base__pixel_swizzler* p,\n                                           wuffs_base__slice_u8 dst,\n                                           wuffs_base__slice_u8 dst_palette,\n                                           wuffs_base__slice_u8 src) {\n  if (p && p->private_impl.func) {\n    return (*(p->private_impl.func))(dst, dst_palette, src);\n  }\n  return 0;\n}\n" +
+	"// ---------------- Images\n\nconst uint32_t wuffs_base__pixel_format__bits_per_channel[16] = {\n    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,\n    0x08, 0x0A, 0x0C, 0x10, 0x18, 0x20, 0x30, 0x40,\n};\n\nstatic uint64_t  //\nwuffs_base__pixel_swizzler__copy_1_1(wuffs_base__slice_u8 dst,\n                                     wuffs_base__slice_u8 dst_palette,\n                                     wuffs_base__slice_u8 src) {\n  return wuffs_base__slice_u8__copy_from_slice(dst, src);\n}\n\nstatic uint64_t  //\nwuffs_base__pixel_swizzler__copy_3_1(wuffs_base__slice_u8 dst,\n                                     wuffs_base__slice_u8 dst_palette,\n                                     wuffs_base__slice_u8 src) {\n  if (dst_palette.len != 1024) {\n    return 0;\n  }\n  size_t dst_len3 = dst.len / 3;\n  size_t len = dst_len3 < src.len ? dst_len3 : src.len;\n  uint8_t* d = dst.ptr;\n  uint8_t* s = src.ptr;\n  size_t n = len;\n\n  // N is the loop unroll count.\n  const int N = 4;\n\n  // The comparison in the while condition is \">\", not \">=\", be" +
+	"cause with \">=\",\n  // the last 4-byte store could write past the end of the dst slice.\n  //\n  // Each 4-byte store writes one too many bytes, but a subsequent store will\n  // overwrite that with the correct byte. There is always another store,\n  // whether a 4-byte store in this loop or a 1-byte store in the next loop.\n  while (n > N) {\n    wuffs_base__store_u32le(\n        d + (0 * 3),\n        wuffs_base__load_u32le(dst_palette.ptr + ((uint32_t)(s[0]) * 4)));\n    wuffs_base__store_u32le(\n        d + (1 * 3),\n        wuffs_base__load_u32le(dst_palette.ptr + ((uint32_t)(s[1]) * 4)));\n    wuffs_base__store_u32le(\n        d + (2 * 3),\n        wuffs_base__load_u32le(dst_palette.ptr + ((uint32_t)(s[2]) * 4)));\n    wuffs_base__store_u32le(\n        d + (3 * 3),\n        wuffs_base__load_u32le(dst_palette.ptr + ((uint32_t)(s[3]) * 4)));\n\n    s += 1 * N;\n    d += 3 * N;\n    n -= (size_t)(1 * N);\n  }\n\n  while (n >= 1) {\n    uint32_t color =\n        wuffs_base__load_u32le(dst_palette.ptr + ((uint32_t)(s[0]) * 4));\n    d[0" +
+	"] = (uint8_t)(color >> 0);\n    d[1] = (uint8_t)(color >> 8);\n    d[2] = (uint8_t)(color >> 16);\n\n    s += 1 * 1;\n    d += 3 * 1;\n    n -= (size_t)(1 * 1);\n  }\n\n  return len;\n}\nstatic uint64_t  //\nwuffs_base__pixel_swizzler__copy_4_1(wuffs_base__slice_u8 dst,\n                                     wuffs_base__slice_u8 dst_palette,\n                                     wuffs_base__slice_u8 src) {\n  if (dst_palette.len != 1024) {\n    return 0;\n  }\n  size_t dst_len4 = dst.len / 4;\n  size_t len = dst_len4 < src.len ? dst_len4 : src.len;\n  uint8_t* d = dst.ptr;\n  uint8_t* s = src.ptr;\n  size_t n = len;\n\n  // N is the loop unroll count.\n  const int N = 4;\n\n  while (n >= N) {\n    wuffs_base__store_u32le(\n        d + (0 * 4),\n        wuffs_base__load_u32le(dst_palette.ptr + ((uint32_t)(s[0]) * 4)));\n    wuffs_base__store_u32le(\n        d + (1 * 4),\n        wuffs_base__load_u32le(dst_palette.ptr + ((uint32_t)(s[1]) * 4)));\n    wuffs_base__store_u32le(\n        d + (2 * 4),\n        wuffs_base__load_u32le(dst_palette.ptr + (" +
+	"(uint32_t)(s[2]) * 4)));\n    wuffs_base__store_u32le(\n        d + (3 * 4),\n        wuffs_base__load_u32le(dst_palette.ptr + ((uint32_t)(s[3]) * 4)));\n\n    s += 1 * N;\n    d += 4 * N;\n    n -= (size_t)(1 * N);\n  }\n\n  while (n >= 1) {\n    wuffs_base__store_u32le(\n        d + (0 * 4),\n        wuffs_base__load_u32le(dst_palette.ptr + ((uint32_t)(s[0]) * 4)));\n\n    s += 1 * 1;\n    d += 4 * 1;\n    n -= (size_t)(1 * 1);\n  }\n\n  return len;\n}\n\nstatic uint64_t  //\nwuffs_base__pixel_swizzler__swap_rgbx_bgrx(wuffs_base__slice_u8 dst,\n                                           wuffs_base__slice_u8 src) {\n  size_t len4 = (dst.len < src.len ? dst.len : src.len) / 4;\n  uint8_t* d = dst.ptr;\n  uint8_t* s = src.ptr;\n\n  size_t n = len4;\n  while (n--) {\n    uint8_t b0 = s[0];\n    uint8_t b1 = s[1];\n    uint8_t b2 = s[2];\n    uint8_t b3 = s[3];\n    d[0] = b2;\n    d[1] = b1;\n    d[2] = b0;\n    d[3] = b3;\n    s += 4;\n    d += 4;\n  }\n  return len4 * 4;\n}\n\nwuffs_base__status  //\nwuffs_base__pixel_swizzler__prepare(wuffs_base__pixel_s" +
+	"wizzler* p,\n                                    wuffs_base__pixel_format dst_format,\n                                    wuffs_base__slice_u8 dst_palette,\n                                    wuffs_base__pixel_format src_format,\n                                    wuffs_base__slice_u8 src_palette) {\n  if (!p) {\n    return wuffs_base__error__bad_receiver;\n  }\n\n  // TODO: support many more formats.\n\n  uint64_t (*func)(wuffs_base__slice_u8 dst, wuffs_base__slice_u8 dst_palette,\n                   wuffs_base__slice_u8 src) = NULL;\n\n  switch (src_format) {\n    case WUFFS_BASE__PIXEL_FORMAT__INDEXED__BGRA_BINARY:\n      switch (dst_format) {\n        case WUFFS_BASE__PIXEL_FORMAT__INDEXED__BGRA_NONPREMUL:\n        case WUFFS_BASE__PIXEL_FORMAT__INDEXED__BGRA_PREMUL:\n        case WUFFS_BASE__PIXEL_FORMAT__INDEXED__BGRA_BINARY:\n          if (wuffs_base__slice_u8__copy_from_slice(dst_palette, src_palette) !=\n              1024) {\n            break;\n          }\n          func = wuffs_base__pixel_swizzler__copy_1_1;\n       " +
+	"   break;\n        case WUFFS_BASE__PIXEL_FORMAT__BGR:\n          if (wuffs_base__slice_u8__copy_from_slice(dst_palette, src_palette) !=\n              1024) {\n            break;\n          }\n          func = wuffs_base__pixel_swizzler__copy_3_1;\n          break;\n        case WUFFS_BASE__PIXEL_FORMAT__BGRA_NONPREMUL:\n        case WUFFS_BASE__PIXEL_FORMAT__BGRA_PREMUL:\n        case WUFFS_BASE__PIXEL_FORMAT__BGRA_BINARY:\n          if (wuffs_base__slice_u8__copy_from_slice(dst_palette, src_palette) !=\n              1024) {\n            break;\n          }\n          func = wuffs_base__pixel_swizzler__copy_4_1;\n          break;\n        case WUFFS_BASE__PIXEL_FORMAT__RGB:\n          if (wuffs_base__pixel_swizzler__swap_rgbx_bgrx(dst_palette,\n                                                         src_palette) != 1024) {\n            break;\n          }\n          func = wuffs_base__pixel_swizzler__copy_3_1;\n          break;\n        case WUFFS_BASE__PIXEL_FORMAT__RGBA_NONPREMUL:\n        case WUFFS_BASE__PIXEL_FORMAT__RGBA_PR" +
+	"EMUL:\n        case WUFFS_BASE__PIXEL_FORMAT__RGBA_BINARY:\n          if (wuffs_base__pixel_swizzler__swap_rgbx_bgrx(dst_palette,\n                                                         src_palette) != 1024) {\n            break;\n          }\n          func = wuffs_base__pixel_swizzler__copy_4_1;\n          break;\n        default:\n          break;\n      }\n      break;\n\n    default:\n      break;\n  }\n\n  p->private_impl.func = func;\n  return func ? NULL : wuffs_base__error__unsupported_option;\n}\n\nuint64_t  //\nwuffs_base__pixel_swizzler__swizzle_interleaved(\n    const wuffs_base__pixel_swizzler* p,\n    wuffs_base__slice_u8 dst,\n    wuffs_base__slice_u8 dst_palette,\n    wuffs_base__slice_u8 src) {\n  if (p && p->private_impl.func) {\n    return (*(p->private_impl.func))(dst, dst_palette, src);\n  }\n  return 0;\n}\n" +
 	""
 
 const baseCorePrivateH = "" +
@@ -71,6 +73,8 @@
 	"// --------\n\n// A status is either NULL (meaning OK) or a string message. That message is\n// human-readable, for programmers, but it is not for end users. It is not\n// localized, and does not contain additional contextual information such as a\n// source filename.\n//\n// Status strings are statically allocated and should never be free'd. They can\n// be compared by the == operator and not just by strcmp.\n//\n// Statuses come in four categories:\n//  - OK:          the request was completed, successfully.\n//  - Warnings:    the request was completed, unsuccessfully.\n//  - Suspensions: the request was not completed, but can be re-tried.\n//  - Errors:      the request was not completed, permanently.\n//\n// When a function returns an incomplete status, a suspension means that that\n// function should be called again within a new context, such as after flushing\n// or re-filling an I/O buffer. An error means that an irrecoverable failure\n// state was reached.\ntypedef const char* wuffs_base__status;\n\n// !! INSERT wuffs_bas" +
 	"e__status names.\n\nstatic inline bool  //\nwuffs_base__status__is_complete(wuffs_base__status z) {\n  return (z == NULL) || ((*z != '$') && (*z != '#'));\n}\n\nstatic inline bool  //\nwuffs_base__status__is_error(wuffs_base__status z) {\n  return z && (*z == '#');\n}\n\nstatic inline bool  //\nwuffs_base__status__is_ok(wuffs_base__status z) {\n  return z == NULL;\n}\n\nstatic inline bool  //\nwuffs_base__status__is_suspension(wuffs_base__status z) {\n  return z && (*z == '$');\n}\n\nstatic inline bool  //\nwuffs_base__status__is_warning(wuffs_base__status z) {\n  return z && (*z != '$') && (*z != '#');\n}\n\n" +
 	"" +
+	"// --------\n\n// FourCC constants.\n\n// !! INSERT FourCCs.\n\n" +
+	"" +
 	"// --------\n\n// Flicks are a unit of time. One flick (frame-tick) is 1 / 705_600_000 of a\n// second. See https://github.com/OculusVR/Flicks\ntypedef int64_t wuffs_base__flicks;\n\n#define WUFFS_BASE__FLICKS_PER_SECOND ((uint64_t)705600000)\n#define WUFFS_BASE__FLICKS_PER_MILLISECOND ((uint64_t)705600)\n\n" +
 	"" +
 	"// ---------------- Numeric Types\n\nstatic inline uint8_t  //\nwuffs_base__u8__min(uint8_t x, uint8_t y) {\n  return x < y ? x : y;\n}\n\nstatic inline uint8_t  //\nwuffs_base__u8__max(uint8_t x, uint8_t y) {\n  return x > y ? x : y;\n}\n\nstatic inline uint16_t  //\nwuffs_base__u16__min(uint16_t x, uint16_t y) {\n  return x < y ? x : y;\n}\n\nstatic inline uint16_t  //\nwuffs_base__u16__max(uint16_t x, uint16_t y) {\n  return x > y ? x : y;\n}\n\nstatic inline uint32_t  //\nwuffs_base__u32__min(uint32_t x, uint32_t y) {\n  return x < y ? x : y;\n}\n\nstatic inline uint32_t  //\nwuffs_base__u32__max(uint32_t x, uint32_t y) {\n  return x > y ? x : y;\n}\n\nstatic inline uint64_t  //\nwuffs_base__u64__min(uint64_t x, uint64_t y) {\n  return x < y ? x : y;\n}\n\nstatic inline uint64_t  //\nwuffs_base__u64__max(uint64_t x, uint64_t y) {\n  return x > y ? x : y;\n}\n\n" +
@@ -101,80 +105,81 @@
 const baseImagePublicH = "" +
 	"// ---------------- Images\n\n// wuffs_base__color_u32_argb_premul is an 8 bit per channel premultiplied\n// Alpha, Red, Green, Blue color, as a uint32_t value. It is in word order, not\n// byte order: its value is always 0xAARRGGBB, regardless of endianness.\ntypedef uint32_t wuffs_base__color_u32_argb_premul;\n\n" +
 	"" +
-	"// --------\n\n// wuffs_base__pixel_format encodes the format of the bytes that constitute an\n// image frame's pixel data. Its bits:\n//  - bit        31  is reserved.\n//  - bits 30 .. 28 encodes color (and channel order, in terms of memory).\n//  - bit        27  is reserved.\n//  - bits 26 .. 24 encodes transparency.\n//  - bits 23 .. 21 are reserved.\n//  - bit        20 indicates big-endian/MSB-first (as opposed to little/LSB).\n//  - bit        19 indicates floating point (as opposed to integer).\n//  - bit        18 indicates palette-indexed. The number-of-planes (the next\n//                  field) will be zero, as the format is considered packed,\n//                  but the 8-bit N-BGRA color data is stored in plane 3.\n//  - bits 17 .. 16 are the number of planes, minus 1. Zero means packed.\n//  - bits 15 .. 12 encodes the number of bits (depth) in the 3rd channel.\n//  - bits 11 ..  8 encodes the number of bits (depth) in the 2nd channel.\n//  - bits  7 ..  4 encodes the number of bits (depth) in the 1st channe" +
-	"l.\n//  - bits  3 ..  0 encodes the number of bits (depth) in the 0th channel.\n//\n// The bit fields of a wuffs_base__pixel_format are not independent. For\n// example, the number of planes should not be greater than the number of\n// channels. Similarly, bits 15..4 are unused (and should be zero) if bits\n// 31..24 (color and transparency) together imply only 1 channel (gray, no\n// alpha) and floating point samples should mean a bit depth of 16, 32 or 64.\n//\n// Formats hold between 1 and 4 channels. For example: Y (1 channel: gray), YA\n// (2 channels: gray and alpha), BGR (3 channels: blue, green, red) or CMYK (4\n// channels: cyan, magenta, yellow, black).\n//\n// For direct formats with N > 1 channels, those channels can be laid out in\n// either 1 (packed) or N (planar) planes. For example, RGBA data is usually\n// packed, but YCbCr data is usually planar, due to chroma subsampling (for\n// details, see the wuffs_base__pixel_subsampling type).\n//\n// For indexed formats, the palette (always 256 × 4 bytes) holds 8 bi" +
-	"ts per\n// channel non-alpha-premultiplied BGRA color data. There is only 1 plane (for\n// the index), as the format is considered packed. Plane 0 holds the per-pixel\n// indices. Plane 3 is re-purposed to hold the per-index colors.\n//\n// The color field is encoded in 3 bits:\n//  - 0 means                   A (Alpha).\n//  - 1 means Y         or     YA (Gray, Alpha).\n//  - 2 means YCbCr     or YCbCrA (Luma, Chroma-blue, Chroma-red, Alpha).\n//  - 3 means YCoCg     or YCoCgA (Luma, Chroma-orange, Chroma-green, Alpha).\n//  - 4 means BGR, BGRX or   BGRA (Blue, Green, Red, X-padding or Alpha).\n//  - 5 means RGB, RGBX or   RGBA (Red, Green, Blue, X-padding or Alpha).\n//  - 6 means CMY       or   CMYK (Cyan, Magenta, Yellow, Black).\n//  - all other values are reserved.\n//\n// In Wuffs, channels are given in memory order (also known as byte order),\n// regardless of endianness, since the C type for the pixel data is an array of\n// bytes, not an array of uint32_t. For example, packed BGRA with 8 bits per\n// channel means th" +
-	"at the bytes in memory are always Blue, Green, Red then\n// Alpha. On big-endian systems, that is the uint32_t 0xBBGGRRAA. On\n// little-endian, 0xAARRGGBB.\n//\n// When the color field (3 bits) encodes multiple options, the transparency\n// field (3 bits) distinguishes them:\n//  - 0 means fully opaque, no extra channels\n//  - 1 means fully opaque, one extra channel (X or K, padding or black).\n//  - 5 means one extra alpha channel, other channels are non-premultiplied.\n//  - 6 means one extra alpha channel, other channels are     premultiplied.\n//  - 7 means one extra alpha channel, binary alpha.\n//  - all other values are reserved.\n//\n// Binary alpha means that if a color is not completely opaque, it is\n// completely transparent black. As a source pixel format, it can therefore be\n// treated as either non-premultiplied or premultiplied.\n//\n// The zero wuffs_base__pixel_format value is an invalid pixel format, as it is\n// invalid to combine the zero color (alpha only) with the zero transparency.\n//\n// Bit depth is" +
-	" encoded in 4 bits:\n//  -  0 means the channel or index is unused.\n//  -  x means a bit depth of  x, for x in the range 1..8.\n//  -  9 means a bit depth of 10.\n//  - 10 means a bit depth of 12.\n//  - 11 means a bit depth of 16.\n//  - 12 means a bit depth of 24.\n//  - 13 means a bit depth of 32.\n//  - 14 means a bit depth of 48.\n//  - 15 means a bit depth of 64.\n//\n// For example, wuffs_base__pixel_format 0x5510BBBB is a natural format for\n// decoding a PNG image - network byte order (also known as big-endian),\n// packed, non-premultiplied alpha - that happens to be 16-bit-depth truecolor\n// with alpha (RGBA). In memory order:\n//\n//  ptr+0  ptr+1  ptr+2  ptr+3  ptr+4  ptr+5  ptr+6  ptr+7\n//  Rhi    Rlo    Ghi    Glo    Bhi    Blo    Ahi    Alo\n//\n// For example, the value wuffs_base__pixel_format 0x40000565 means BGR with no\n// alpha or padding, 5/6/5 bits for blue/green/red, packed 2 bytes per pixel,\n// laid out LSB-first in memory order:\n//\n//  ptr+0...........  ptr+1...........\n//  MSB          LSB  MSB    " +
-	"      LSB\n//  G₂G₁G₀B₄B₃B₂B₁B₀  R₄R₃R₂R₁R₀G₅G₄G₃\n//\n// On little-endian systems (but not big-endian), this Wuffs pixel format value\n// (0x40000565) corresponds to the Cairo library's CAIRO_FORMAT_RGB16_565, the\n// SDL2 (Simple DirectMedia Layer 2) library's SDL_PIXELFORMAT_RGB565 and the\n// Skia library's kRGB_565_SkColorType. Note BGR in Wuffs versus RGB in the\n// other libraries.\n//\n// Regardless of endianness, this Wuffs pixel format value (0x40000565)\n// corresponds to the V4L2 (Video For Linux 2) library's V4L2_PIX_FMT_RGB565\n// and the Wayland-DRM library's WL_DRM_FORMAT_RGB565.\n//\n// Different software libraries name their pixel formats (and especially their\n// channel order) either according to memory layout or as bits of a native\n// integer type like uint32_t. The two conventions differ because of a system's\n// endianness. As mentioned earlier, Wuffs pixel formats are always in memory\n// order. More detail of other software libraries' naming conventions is in the\n// Pi" +
-	"xel Format Guide at https://afrantzis.github.io/pixel-format-guide/\n//\n// Do not manipulate these bits directly; they are private implementation\n// details. Use methods such as wuffs_base__pixel_format__num_planes instead.\ntypedef uint32_t wuffs_base__pixel_format;\n\n// Common 8-bit-depth pixel formats. This list is not exhaustive; not all valid\n// wuffs_base__pixel_format values are present.\n\n#define WUFFS_BASE__PIXEL_FORMAT__INVALID ((wuffs_base__pixel_format)0x00000000)\n\n#define WUFFS_BASE__PIXEL_FORMAT__A ((wuffs_base__pixel_format)0x02000008)\n\n#define WUFFS_BASE__PIXEL_FORMAT__Y ((wuffs_base__pixel_format)0x10000008)\n#define WUFFS_BASE__PIXEL_FORMAT__YA_NONPREMUL \\\n  ((wuffs_base__pixel_format)0x15000008)\n#define WUFFS_BASE__PIXEL_FORMAT__YA_PREMUL \\\n  ((wuffs_base__pixel_format)0x16000008)\n\n#define WUFFS_BASE__PIXEL_FORMAT__YCBCR ((wuffs_base__pixel_format)0x20020888)\n#define WUFFS_BASE__PIXEL_FORMAT__YCBCRK ((wuffs_base__pixel_format)0x21038888)\n#define WUFFS_BASE__PIXEL_FORMAT__YCBCRA_NONPREMUL \\\n  ((w" +
-	"uffs_base__pixel_format)0x25038888)\n\n#define WUFFS_BASE__PIXEL_FORMAT__YCOCG ((wuffs_base__pixel_format)0x30020888)\n#define WUFFS_BASE__PIXEL_FORMAT__YCOCGK ((wuffs_base__pixel_format)0x31038888)\n#define WUFFS_BASE__PIXEL_FORMAT__YCOCGA_NONPREMUL \\\n  ((wuffs_base__pixel_format)0x35038888)\n\n#define WUFFS_BASE__PIXEL_FORMAT__INDEXED__BGRA_NONPREMUL \\\n  ((wuffs_base__pixel_format)0x45040008)\n#define WUFFS_BASE__PIXEL_FORMAT__INDEXED__BGRA_PREMUL \\\n  ((wuffs_base__pixel_format)0x46040008)\n#define WUFFS_BASE__PIXEL_FORMAT__INDEXED__BGRA_BINARY \\\n  ((wuffs_base__pixel_format)0x47040008)\n\n#define WUFFS_BASE__PIXEL_FORMAT__BGR ((wuffs_base__pixel_format)0x40000888)\n#define WUFFS_BASE__PIXEL_FORMAT__BGRX ((wuffs_base__pixel_format)0x41008888)\n#define WUFFS_BASE__PIXEL_FORMAT__BGRA_NONPREMUL \\\n  ((wuffs_base__pixel_format)0x45008888)\n#define WUFFS_BASE__PIXEL_FORMAT__BGRA_PREMUL \\\n  ((wuffs_base__pixel_format)0x46008888)\n#define WUFFS_BASE__PIXEL_FORMAT__BGRA_BINARY \\\n  ((wuffs_base__pixel_format)0x47008888)\n\n#define W" +
-	"UFFS_BASE__PIXEL_FORMAT__RGB ((wuffs_base__pixel_format)0x50000888)\n#define WUFFS_BASE__PIXEL_FORMAT__RGBX ((wuffs_base__pixel_format)0x51008888)\n#define WUFFS_BASE__PIXEL_FORMAT__RGBA_NONPREMUL \\\n  ((wuffs_base__pixel_format)0x55008888)\n#define WUFFS_BASE__PIXEL_FORMAT__RGBA_PREMUL \\\n  ((wuffs_base__pixel_format)0x56008888)\n#define WUFFS_BASE__PIXEL_FORMAT__RGBA_BINARY \\\n  ((wuffs_base__pixel_format)0x57008888)\n\n#define WUFFS_BASE__PIXEL_FORMAT__CMY ((wuffs_base__pixel_format)0x60020888)\n#define WUFFS_BASE__PIXEL_FORMAT__CMYK ((wuffs_base__pixel_format)0x61038888)\n\nextern const uint32_t wuffs_base__pixel_format__bits_per_channel[16];\n\nstatic inline bool  //\nwuffs_base__pixel_format__is_valid(wuffs_base__pixel_format f) {\n  return f != 0;\n}\n\n// wuffs_base__pixel_format__bits_per_pixel returns, for packed pixel formats,\n// the number of bits per pixel. It returns 0 for planar pixel formats.\nstatic inline uint32_t  //\nwuffs_base__pixel_format__bits_per_pixel(wuffs_base__pixel_format f) {\n  if (((f >> 16) & 0x03" +
-	") != 0) {\n    return 0;\n  }\n  return wuffs_base__pixel_format__bits_per_channel[0x0F & (f >> 0)] +\n         wuffs_base__pixel_format__bits_per_channel[0x0F & (f >> 4)] +\n         wuffs_base__pixel_format__bits_per_channel[0x0F & (f >> 8)] +\n         wuffs_base__pixel_format__bits_per_channel[0x0F & (f >> 12)];\n}\n\nstatic inline bool  //\nwuffs_base__pixel_format__is_indexed(wuffs_base__pixel_format f) {\n  return (f >> 18) & 0x01;\n}\n\nstatic inline bool  //\nwuffs_base__pixel_format__is_packed(wuffs_base__pixel_format f) {\n  return ((f >> 16) & 0x03) == 0;\n}\n\nstatic inline bool  //\nwuffs_base__pixel_format__is_planar(wuffs_base__pixel_format f) {\n  return ((f >> 16) & 0x03) != 0;\n}\n\nstatic inline uint32_t  //\nwuffs_base__pixel_format__num_planes(wuffs_base__pixel_format f) {\n  return ((f >> 16) & 0x03) + 1;\n}\n\n#define WUFFS_BASE__PIXEL_FORMAT__NUM_PLANES_MAX 4\n\n#define WUFFS_BASE__PIXEL_FORMAT__INDEXED__INDEX_PLANE 0\n#define WUFFS_BASE__PIXEL_FORMAT__INDEXED__COLOR_PLANE 3\n\n" +
+	"// --------\n\n// wuffs_base__pixel_format encodes the format of the bytes that constitute an\n// image frame's pixel data. Its bits:\n//  - bit        31  is reserved.\n//  - bits 30 .. 28 encodes color (and channel order, in terms of memory).\n//  - bit        27  is reserved.\n//  - bits 26 .. 24 encodes transparency.\n//  - bits 23 .. 21 are reserved.\n//  - bit        20 indicates big-endian/MSB-first (as opposed to little/LSB).\n//  - bit        19 indicates floating point (as opposed to integer).\n//  - bit        18 indicates palette-indexed. The number-of-planes (the next\n//                  field) will be 0, as the format is considered interleaved,\n//                  but the 8-bit N-BGRA color data is stored in plane 3.\n//  - bits 17 .. 16 are the number of planes, minus 1. Zero means interleaved.\n//  - bits 15 .. 12 encodes the number of bits (depth) in the 3rd channel.\n//  - bits 11 ..  8 encodes the number of bits (depth) in the 2nd channel.\n//  - bits  7 ..  4 encodes the number of bits (depth) in the 1st" +
+	" channel.\n//  - bits  3 ..  0 encodes the number of bits (depth) in the 0th channel.\n//\n// The bit fields of a wuffs_base__pixel_format are not independent. For\n// example, the number of planes should not be greater than the number of\n// channels. Similarly, bits 15..4 are unused (and should be zero) if bits\n// 31..24 (color and transparency) together imply only 1 channel (gray, no\n// alpha) and floating point samples should mean a bit depth of 16, 32 or 64.\n//\n// Formats hold between 1 and 4 channels. For example: Y (1 channel: gray), YA\n// (2 channels: gray and alpha), BGR (3 channels: blue, green, red) or CMYK (4\n// channels: cyan, magenta, yellow, black).\n//\n// For direct formats with N > 1 channels, those channels can be laid out in\n// either 1 (interleaved) or N (planar) planes. For example, RGBA data is\n// usually interleaved, but YCbCr data is usually planar, due to chroma\n// subsampling (for details, see the wuffs_base__pixel_subsampling type).\n//\n// For indexed formats, the palette (always 256 × 4 " +
+	"bytes) holds 8 bits per\n// channel non-alpha-premultiplied BGRA color data. There is only 1 plane (for\n// the index), as the format is considered interleaved. Plane 0 holds the\n// per-pixel indices. Plane 3 is re-purposed to hold the per-index colors.\n//\n// The color field is encoded in 3 bits:\n//  - 0 means                   A (Alpha).\n//  - 1 means Y         or     YA (Gray, Alpha).\n//  - 2 means YCbCr     or YCbCrA (Luma, Chroma-blue, Chroma-red, Alpha).\n//  - 3 means YCoCg     or YCoCgA (Luma, Chroma-orange, Chroma-green, Alpha).\n//  - 4 means BGR, BGRX or   BGRA (Blue, Green, Red, X-padding or Alpha).\n//  - 5 means RGB, RGBX or   RGBA (Red, Green, Blue, X-padding or Alpha).\n//  - 6 means CMY       or   CMYK (Cyan, Magenta, Yellow, Black).\n//  - all other values are reserved.\n//\n// In Wuffs, channels are given in memory order (also known as byte order),\n// regardless of endianness, since the C type for the pixel data is an array of\n// bytes, not an array of uint32_t. For example, interleaved BGRA with 8 b" +
+	"its\n// per channel means that the bytes in memory are always Blue, Green, Red then\n// Alpha. On big-endian systems, that is the uint32_t 0xBBGGRRAA. On\n// little-endian, 0xAARRGGBB.\n//\n// When the color field (3 bits) encodes multiple options, the transparency\n// field (3 bits) distinguishes them:\n//  - 0 means fully opaque, no extra channels\n//  - 1 means fully opaque, one extra channel (X or K, padding or black).\n//  - 5 means one extra alpha channel, other channels are non-premultiplied.\n//  - 6 means one extra alpha channel, other channels are     premultiplied.\n//  - 7 means one extra alpha channel, binary alpha.\n//  - all other values are reserved.\n//\n// Binary alpha means that if a color is not completely opaque, it is\n// completely transparent black. As a source pixel format, it can therefore be\n// treated as either non-premultiplied or premultiplied.\n//\n// The zero wuffs_base__pixel_format value is an invalid pixel format, as it is\n// invalid to combine the zero color (alpha only) with the zero trans" +
+	"parency.\n//\n// Bit depth is encoded in 4 bits:\n//  -  0 means the channel or index is unused.\n//  -  x means a bit depth of  x, for x in the range 1..8.\n//  -  9 means a bit depth of 10.\n//  - 10 means a bit depth of 12.\n//  - 11 means a bit depth of 16.\n//  - 12 means a bit depth of 24.\n//  - 13 means a bit depth of 32.\n//  - 14 means a bit depth of 48.\n//  - 15 means a bit depth of 64.\n//\n// For example, wuffs_base__pixel_format 0x5510BBBB is a natural format for\n// decoding a PNG image - network byte order (also known as big-endian),\n// interleaved, non-premultiplied alpha - that happens to be 16-bit-depth\n// truecolor with alpha (RGBA). In memory order:\n//\n//  ptr+0  ptr+1  ptr+2  ptr+3  ptr+4  ptr+5  ptr+6  ptr+7\n//  Rhi    Rlo    Ghi    Glo    Bhi    Blo    Ahi    Alo\n//\n// For example, the value wuffs_base__pixel_format 0x40000565 means BGR with no\n// alpha or padding, 5/6/5 bits for blue/green/red, interleaved 2 bytes per\n// pixel, laid out LSB-first in memory order:\n//\n//  ptr+0...........  ptr+1...." +
+	".......\n//  MSB          LSB  MSB          LSB\n//  G₂G₁G₀B₄B₃B₂B₁B₀  R₄R₃R₂R₁R₀G₅G₄G₃\n//\n// On little-endian systems (but not big-endian), this Wuffs pixel format value\n// (0x40000565) corresponds to the Cairo library's CAIRO_FORMAT_RGB16_565, the\n// SDL2 (Simple DirectMedia Layer 2) library's SDL_PIXELFORMAT_RGB565 and the\n// Skia library's kRGB_565_SkColorType. Note BGR in Wuffs versus RGB in the\n// other libraries.\n//\n// Regardless of endianness, this Wuffs pixel format value (0x40000565)\n// corresponds to the V4L2 (Video For Linux 2) library's V4L2_PIX_FMT_RGB565\n// and the Wayland-DRM library's WL_DRM_FORMAT_RGB565.\n//\n// Different software libraries name their pixel formats (and especially their\n// channel order) either according to memory layout or as bits of a native\n// integer type like uint32_t. The two conventions differ because of a system's\n// endianness. As mentioned earlier, Wuffs pixel formats are always in memory\n// order. More detail of other software librarie" +
+	"s' naming conventions is in the\n// Pixel Format Guide at https://afrantzis.github.io/pixel-format-guide/\n//\n// Do not manipulate these bits directly; they are private implementation\n// details. Use methods such as wuffs_base__pixel_format__num_planes instead.\ntypedef uint32_t wuffs_base__pixel_format;\n\n// Common 8-bit-depth pixel formats. This list is not exhaustive; not all valid\n// wuffs_base__pixel_format values are present.\n\n#define WUFFS_BASE__PIXEL_FORMAT__INVALID ((wuffs_base__pixel_format)0x00000000)\n\n#define WUFFS_BASE__PIXEL_FORMAT__A ((wuffs_base__pixel_format)0x02000008)\n\n#define WUFFS_BASE__PIXEL_FORMAT__Y ((wuffs_base__pixel_format)0x10000008)\n#define WUFFS_BASE__PIXEL_FORMAT__YA_NONPREMUL \\\n  ((wuffs_base__pixel_format)0x15000008)\n#define WUFFS_BASE__PIXEL_FORMAT__YA_PREMUL \\\n  ((wuffs_base__pixel_format)0x16000008)\n\n#define WUFFS_BASE__PIXEL_FORMAT__YCBCR ((wuffs_base__pixel_format)0x20020888)\n#define WUFFS_BASE__PIXEL_FORMAT__YCBCRK ((wuffs_base__pixel_format)0x21038888)\n#define WUFFS_BASE__P" +
+	"IXEL_FORMAT__YCBCRA_NONPREMUL \\\n  ((wuffs_base__pixel_format)0x25038888)\n\n#define WUFFS_BASE__PIXEL_FORMAT__YCOCG ((wuffs_base__pixel_format)0x30020888)\n#define WUFFS_BASE__PIXEL_FORMAT__YCOCGK ((wuffs_base__pixel_format)0x31038888)\n#define WUFFS_BASE__PIXEL_FORMAT__YCOCGA_NONPREMUL \\\n  ((wuffs_base__pixel_format)0x35038888)\n\n#define WUFFS_BASE__PIXEL_FORMAT__INDEXED__BGRA_NONPREMUL \\\n  ((wuffs_base__pixel_format)0x45040008)\n#define WUFFS_BASE__PIXEL_FORMAT__INDEXED__BGRA_PREMUL \\\n  ((wuffs_base__pixel_format)0x46040008)\n#define WUFFS_BASE__PIXEL_FORMAT__INDEXED__BGRA_BINARY \\\n  ((wuffs_base__pixel_format)0x47040008)\n\n#define WUFFS_BASE__PIXEL_FORMAT__BGR ((wuffs_base__pixel_format)0x40000888)\n#define WUFFS_BASE__PIXEL_FORMAT__BGRX ((wuffs_base__pixel_format)0x41008888)\n#define WUFFS_BASE__PIXEL_FORMAT__BGRA_NONPREMUL \\\n  ((wuffs_base__pixel_format)0x45008888)\n#define WUFFS_BASE__PIXEL_FORMAT__BGRA_PREMUL \\\n  ((wuffs_base__pixel_format)0x46008888)\n#define WUFFS_BASE__PIXEL_FORMAT__BGRA_BINARY \\\n  ((wuffs_base" +
+	"__pixel_format)0x47008888)\n\n#define WUFFS_BASE__PIXEL_FORMAT__RGB ((wuffs_base__pixel_format)0x50000888)\n#define WUFFS_BASE__PIXEL_FORMAT__RGBX ((wuffs_base__pixel_format)0x51008888)\n#define WUFFS_BASE__PIXEL_FORMAT__RGBA_NONPREMUL \\\n  ((wuffs_base__pixel_format)0x55008888)\n#define WUFFS_BASE__PIXEL_FORMAT__RGBA_PREMUL \\\n  ((wuffs_base__pixel_format)0x56008888)\n#define WUFFS_BASE__PIXEL_FORMAT__RGBA_BINARY \\\n  ((wuffs_base__pixel_format)0x57008888)\n\n#define WUFFS_BASE__PIXEL_FORMAT__CMY ((wuffs_base__pixel_format)0x60020888)\n#define WUFFS_BASE__PIXEL_FORMAT__CMYK ((wuffs_base__pixel_format)0x61038888)\n\nextern const uint32_t wuffs_base__pixel_format__bits_per_channel[16];\n\nstatic inline bool  //\nwuffs_base__pixel_format__is_valid(wuffs_base__pixel_format f) {\n  return f != 0;\n}\n\n// wuffs_base__pixel_format__bits_per_pixel returns the number of bits per\n// pixel for interleaved pixel formats, and returns 0 for planar pixel formats.\nstatic inline uint32_t  //\nwuffs_base__pixel_format__bits_per_pixel(wuffs_base__" +
+	"pixel_format f) {\n  if (((f >> 16) & 0x03) != 0) {\n    return 0;\n  }\n  return wuffs_base__pixel_format__bits_per_channel[0x0F & (f >> 0)] +\n         wuffs_base__pixel_format__bits_per_channel[0x0F & (f >> 4)] +\n         wuffs_base__pixel_format__bits_per_channel[0x0F & (f >> 8)] +\n         wuffs_base__pixel_format__bits_per_channel[0x0F & (f >> 12)];\n}\n\nstatic inline bool  //\nwuffs_base__pixel_format__is_indexed(wuffs_base__pixel_format f) {\n  return (f >> 18) & 0x01;\n}\n\nstatic inline bool  //\nwuffs_base__pixel_format__is_interleaved(wuffs_base__pixel_format f) {\n  return ((f >> 16) & 0x03) == 0;\n}\n\nstatic inline bool  //\nwuffs_base__pixel_format__is_planar(wuffs_base__pixel_format f) {\n  return ((f >> 16) & 0x03) != 0;\n}\n\nstatic inline uint32_t  //\nwuffs_base__pixel_format__num_planes(wuffs_base__pixel_format f) {\n  return ((f >> 16) & 0x03) + 1;\n}\n\n#define WUFFS_BASE__PIXEL_FORMAT__NUM_PLANES_MAX 4\n\n#define WUFFS_BASE__PIXEL_FORMAT__INDEXED__INDEX_PLANE 0\n#define WUFFS_BASE__PIXEL_FORMAT__INDEXED__COLOR_PLA" +
+	"NE 3\n\n" +
 	"" +
-	"// --------\n\n// wuffs_base__pixel_subsampling encodes the mapping of pixel space coordinates\n// (x, y) to pixel buffer indices (i, j). That mapping can differ for each\n// plane p. For a depth of 8 bits (1 byte), the p'th plane's sample starts at\n// (planes[p].ptr + (j * planes[p].stride) + i).\n//\n// For packed pixel formats, the mapping is trivial: i = x and j = y. For\n// planar pixel formats, the mapping can differ due to chroma subsampling. For\n// example, consider a three plane YCbCr pixel format with 4:2:2 subsampling.\n// For the luma (Y) channel, there is one sample for every pixel, but for the\n// chroma (Cb, Cr) channels, there is one sample for every two pixels: pairs of\n// horizontally adjacent pixels form one macropixel, i = x / 2 and j == y. In\n// general, for a given p:\n//  - i = (x + bias_x) >> shift_x.\n//  - j = (y + bias_y) >> shift_y.\n// where biases and shifts are in the range 0..3 and 0..2 respectively.\n//\n// In general, the biases will be zero after decoding an image. However, making\n// a su" +
-	"b-image may change the bias, since the (x, y) coordinates are relative\n// to the sub-image's top-left origin, but the backing pixel buffers were\n// created relative to the original image's origin.\n//\n// For each plane p, each of those four numbers (biases and shifts) are encoded\n// in two bits, which combine to form an 8 bit unsigned integer:\n//\n//  e_p = (bias_x << 6) | (shift_x << 4) | (bias_y << 2) | (shift_y << 0)\n//\n// Those e_p values (e_0 for the first plane, e_1 for the second plane, etc)\n// combine to form a wuffs_base__pixel_subsampling value:\n//\n//  pixsub = (e_3 << 24) | (e_2 << 16) | (e_1 << 8) | (e_0 << 0)\n//\n// Do not manipulate these bits directly; they are private implementation\n// details. Use methods such as wuffs_base__pixel_subsampling__bias_x instead.\ntypedef uint32_t wuffs_base__pixel_subsampling;\n\n#define WUFFS_BASE__PIXEL_SUBSAMPLING__NONE ((wuffs_base__pixel_subsampling)0)\n\n#define WUFFS_BASE__PIXEL_SUBSAMPLING__444 \\\n  ((wuffs_base__pixel_subsampling)0x000000)\n#define WUFFS_BASE__PI" +
-	"XEL_SUBSAMPLING__440 \\\n  ((wuffs_base__pixel_subsampling)0x010100)\n#define WUFFS_BASE__PIXEL_SUBSAMPLING__422 \\\n  ((wuffs_base__pixel_subsampling)0x101000)\n#define WUFFS_BASE__PIXEL_SUBSAMPLING__420 \\\n  ((wuffs_base__pixel_subsampling)0x111100)\n#define WUFFS_BASE__PIXEL_SUBSAMPLING__411 \\\n  ((wuffs_base__pixel_subsampling)0x202000)\n#define WUFFS_BASE__PIXEL_SUBSAMPLING__410 \\\n  ((wuffs_base__pixel_subsampling)0x212100)\n\nstatic inline uint32_t  //\nwuffs_base__pixel_subsampling__bias_x(wuffs_base__pixel_subsampling s,\n                                      uint32_t plane) {\n  uint32_t shift = ((plane & 0x03) * 8) + 6;\n  return (s >> shift) & 0x03;\n}\n\nstatic inline uint32_t  //\nwuffs_base__pixel_subsampling__shift_x(wuffs_base__pixel_subsampling s,\n                                       uint32_t plane) {\n  uint32_t shift = ((plane & 0x03) * 8) + 4;\n  return (s >> shift) & 0x03;\n}\n\nstatic inline uint32_t  //\nwuffs_base__pixel_subsampling__bias_y(wuffs_base__pixel_subsampling s,\n                                    " +
-	"  uint32_t plane) {\n  uint32_t shift = ((plane & 0x03) * 8) + 2;\n  return (s >> shift) & 0x03;\n}\n\nstatic inline uint32_t  //\nwuffs_base__pixel_subsampling__shift_y(wuffs_base__pixel_subsampling s,\n                                       uint32_t plane) {\n  uint32_t shift = ((plane & 0x03) * 8) + 0;\n  return (s >> shift) & 0x03;\n}\n\n" +
+	"// --------\n\n// wuffs_base__pixel_subsampling encodes the mapping of pixel space coordinates\n// (x, y) to pixel buffer indices (i, j). That mapping can differ for each\n// plane p. For a depth of 8 bits (1 byte), the p'th plane's sample starts at\n// (planes[p].ptr + (j * planes[p].stride) + i).\n//\n// For interleaved pixel formats, the mapping is trivial: i = x and j = y. For\n// planar pixel formats, the mapping can differ due to chroma subsampling. For\n// example, consider a three plane YCbCr pixel format with 4:2:2 subsampling.\n// For the luma (Y) channel, there is one sample for every pixel, but for the\n// chroma (Cb, Cr) channels, there is one sample for every two pixels: pairs of\n// horizontally adjacent pixels form one macropixel, i = x / 2 and j == y. In\n// general, for a given p:\n//  - i = (x + bias_x) >> shift_x.\n//  - j = (y + bias_y) >> shift_y.\n// where biases and shifts are in the range 0..3 and 0..2 respectively.\n//\n// In general, the biases will be zero after decoding an image. However, making\n//" +
+	" a sub-image may change the bias, since the (x, y) coordinates are relative\n// to the sub-image's top-left origin, but the backing pixel buffers were\n// created relative to the original image's origin.\n//\n// For each plane p, each of those four numbers (biases and shifts) are encoded\n// in two bits, which combine to form an 8 bit unsigned integer:\n//\n//  e_p = (bias_x << 6) | (shift_x << 4) | (bias_y << 2) | (shift_y << 0)\n//\n// Those e_p values (e_0 for the first plane, e_1 for the second plane, etc)\n// combine to form a wuffs_base__pixel_subsampling value:\n//\n//  pixsub = (e_3 << 24) | (e_2 << 16) | (e_1 << 8) | (e_0 << 0)\n//\n// Do not manipulate these bits directly; they are private implementation\n// details. Use methods such as wuffs_base__pixel_subsampling__bias_x instead.\ntypedef uint32_t wuffs_base__pixel_subsampling;\n\n#define WUFFS_BASE__PIXEL_SUBSAMPLING__NONE ((wuffs_base__pixel_subsampling)0)\n\n#define WUFFS_BASE__PIXEL_SUBSAMPLING__444 \\\n  ((wuffs_base__pixel_subsampling)0x000000)\n#define WUFFS_BAS" +
+	"E__PIXEL_SUBSAMPLING__440 \\\n  ((wuffs_base__pixel_subsampling)0x010100)\n#define WUFFS_BASE__PIXEL_SUBSAMPLING__422 \\\n  ((wuffs_base__pixel_subsampling)0x101000)\n#define WUFFS_BASE__PIXEL_SUBSAMPLING__420 \\\n  ((wuffs_base__pixel_subsampling)0x111100)\n#define WUFFS_BASE__PIXEL_SUBSAMPLING__411 \\\n  ((wuffs_base__pixel_subsampling)0x202000)\n#define WUFFS_BASE__PIXEL_SUBSAMPLING__410 \\\n  ((wuffs_base__pixel_subsampling)0x212100)\n\nstatic inline uint32_t  //\nwuffs_base__pixel_subsampling__bias_x(wuffs_base__pixel_subsampling s,\n                                      uint32_t plane) {\n  uint32_t shift = ((plane & 0x03) * 8) + 6;\n  return (s >> shift) & 0x03;\n}\n\nstatic inline uint32_t  //\nwuffs_base__pixel_subsampling__shift_x(wuffs_base__pixel_subsampling s,\n                                       uint32_t plane) {\n  uint32_t shift = ((plane & 0x03) * 8) + 4;\n  return (s >> shift) & 0x03;\n}\n\nstatic inline uint32_t  //\nwuffs_base__pixel_subsampling__bias_y(wuffs_base__pixel_subsampling s,\n                               " +
+	"       uint32_t plane) {\n  uint32_t shift = ((plane & 0x03) * 8) + 2;\n  return (s >> shift) & 0x03;\n}\n\nstatic inline uint32_t  //\nwuffs_base__pixel_subsampling__shift_y(wuffs_base__pixel_subsampling s,\n                                       uint32_t plane) {\n  uint32_t shift = ((plane & 0x03) * 8) + 0;\n  return (s >> shift) & 0x03;\n}\n\n" +
 	"" +
-	"// --------\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__pixel_format pixfmt;\n    wuffs_base__pixel_subsampling pixsub;\n    uint32_t width;\n    uint32_t height;\n  } private_impl;\n\n#ifdef __cplusplus\n  inline void set(wuffs_base__pixel_format pixfmt,\n                  wuffs_base__pixel_subsampling pixsub,\n                  uint32_t width,\n                  uint32_t height);\n  inline void invalidate();\n  inline bool is_valid();\n  inline wuffs_base__pixel_format pixel_format();\n  inline wuffs_base__pixel_subsampling pixel_subsampling();\n  inline wuffs_base__rect_ie_u32 bounds();\n  inline uint32_t width();\n  inline uint32_t height();\n  inline uint64_t pixbuf_len();\n#endif  // __cplusplus\n\n} wuffs_base__pixel_config;\n\nstatic inline wuffs_base__pixel_config  //\nwuffs_base__null_pixel_config() {\n  wuffs_base__pixel_config ret;\n  ret.private_impl.pixfmt = 0;\n  ret.private_impl.pixsub = 0;\n  " +
-	"ret.private_impl.width = 0;\n  ret.private_impl.height = 0;\n  return ret;\n}\n\n// TODO: Should this function return bool? An error type?\nstatic inline void  //\nwuffs_base__pixel_config__set(wuffs_base__pixel_config* c,\n                              wuffs_base__pixel_format pixfmt,\n                              wuffs_base__pixel_subsampling pixsub,\n                              uint32_t width,\n                              uint32_t height) {\n  if (!c) {\n    return;\n  }\n  if (pixfmt) {\n    uint64_t wh = ((uint64_t)width) * ((uint64_t)height);\n    // TODO: handle things other than 1 byte per pixel.\n    if (wh <= ((uint64_t)SIZE_MAX)) {\n      c->private_impl.pixfmt = pixfmt;\n      c->private_impl.pixsub = pixsub;\n      c->private_impl.width = width;\n      c->private_impl.height = height;\n      return;\n    }\n  }\n\n  c->private_impl.pixfmt = 0;\n  c->private_impl.pixsub = 0;\n  c->private_impl.width = 0;\n  c->private_impl.height = 0;\n}\n\nstatic inline void  //\nwuffs_base__pixel_config__invalidate(wuffs_base__pixel_config*" +
-	" c) {\n  if (c) {\n    c->private_impl.pixfmt = 0;\n    c->private_impl.pixsub = 0;\n    c->private_impl.width = 0;\n    c->private_impl.height = 0;\n  }\n}\n\nstatic inline bool  //\nwuffs_base__pixel_config__is_valid(wuffs_base__pixel_config* c) {\n  return c && c->private_impl.pixfmt;\n}\n\nstatic inline wuffs_base__pixel_format  //\nwuffs_base__pixel_config__pixel_format(wuffs_base__pixel_config* c) {\n  return c ? c->private_impl.pixfmt : 0;\n}\n\nstatic inline wuffs_base__pixel_subsampling  //\nwuffs_base__pixel_config__pixel_subsampling(wuffs_base__pixel_config* c) {\n  return c ? c->private_impl.pixsub : 0;\n}\n\nstatic inline wuffs_base__rect_ie_u32  //\nwuffs_base__pixel_config__bounds(wuffs_base__pixel_config* c) {\n  if (c) {\n    wuffs_base__rect_ie_u32 ret;\n    ret.min_incl_x = 0;\n    ret.min_incl_y = 0;\n    ret.max_excl_x = c->private_impl.width;\n    ret.max_excl_y = c->private_impl.height;\n    return ret;\n  }\n\n  wuffs_base__rect_ie_u32 ret;\n  ret.min_incl_x = 0;\n  ret.min_incl_y = 0;\n  ret.max_excl_x = 0;\n  ret.max_excl" +
-	"_y = 0;\n  return ret;\n}\n\nstatic inline uint32_t  //\nwuffs_base__pixel_config__width(wuffs_base__pixel_config* c) {\n  return c ? c->private_impl.width : 0;\n}\n\nstatic inline uint32_t  //\nwuffs_base__pixel_config__height(wuffs_base__pixel_config* c) {\n  return c ? c->private_impl.height : 0;\n}\n\n// TODO: this is the right API for planar (not packed) pixbufs? Should it allow\n// decoding into a color model different from the format's intrinsic one? For\n// example, decoding a JPEG image straight to RGBA instead of to YCbCr?\nstatic inline uint64_t  //\nwuffs_base__pixel_config__pixbuf_len(wuffs_base__pixel_config* c) {\n  if (!c) {\n    return 0;\n  }\n  if (wuffs_base__pixel_format__is_planar(c->private_impl.pixfmt)) {\n    // TODO: support planar pixel formats, concious of pixel subsampling.\n    return 0;\n  }\n  uint32_t bits_per_pixel =\n      wuffs_base__pixel_format__bits_per_pixel(c->private_impl.pixfmt);\n  if ((bits_per_pixel == 0) || ((bits_per_pixel % 8) != 0)) {\n    // TODO: support fraction-of-byte pixels, e.g. 1 " +
-	"bit per pixel?\n    return 0;\n  }\n  uint64_t bytes_per_pixel = bits_per_pixel / 8;\n\n  uint64_t n =\n      ((uint64_t)c->private_impl.width) * ((uint64_t)c->private_impl.height);\n  if (n > (UINT64_MAX / bytes_per_pixel)) {\n    return 0;\n  }\n  n *= bytes_per_pixel;\n\n  if (wuffs_base__pixel_format__is_indexed(c->private_impl.pixfmt)) {\n    if (n > (UINT64_MAX - 1024)) {\n      return 0;\n    }\n    n += 1024;\n  }\n\n  return n;\n}\n\n#ifdef __cplusplus\n\ninline void  //\nwuffs_base__pixel_config::set(wuffs_base__pixel_format pixfmt,\n                              wuffs_base__pixel_subsampling pixsub,\n                              uint32_t width,\n                              uint32_t height) {\n  wuffs_base__pixel_config__set(this, pixfmt, pixsub, width, height);\n}\n\ninline void  //\nwuffs_base__pixel_config::invalidate() {\n  wuffs_base__pixel_config__invalidate(this);\n}\n\ninline bool  //\nwuffs_base__pixel_config::is_valid() {\n  return wuffs_base__pixel_config__is_valid(this);\n}\n\ninline wuffs_base__pixel_format  //\nwuffs_base__p" +
-	"ixel_config::pixel_format() {\n  return wuffs_base__pixel_config__pixel_format(this);\n}\n\ninline wuffs_base__pixel_subsampling  //\nwuffs_base__pixel_config::pixel_subsampling() {\n  return wuffs_base__pixel_config__pixel_subsampling(this);\n}\n\ninline wuffs_base__rect_ie_u32  //\nwuffs_base__pixel_config::bounds() {\n  return wuffs_base__pixel_config__bounds(this);\n}\n\ninline uint32_t  //\nwuffs_base__pixel_config::width() {\n  return wuffs_base__pixel_config__width(this);\n}\n\ninline uint32_t  //\nwuffs_base__pixel_config::height() {\n  return wuffs_base__pixel_config__height(this);\n}\n\ninline uint64_t  //\nwuffs_base__pixel_config::pixbuf_len() {\n  return wuffs_base__pixel_config__pixbuf_len(this);\n}\n\n#endif  // __cplusplus\n\n" +
+	"// --------\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__pixel_format pixfmt;\n    wuffs_base__pixel_subsampling pixsub;\n    uint32_t width;\n    uint32_t height;\n  } private_impl;\n\n#ifdef __cplusplus\n  inline void set(wuffs_base__pixel_format pixfmt,\n                  wuffs_base__pixel_subsampling pixsub,\n                  uint32_t width,\n                  uint32_t height);\n  inline void invalidate();\n  inline bool is_valid() const;\n  inline wuffs_base__pixel_format pixel_format() const;\n  inline wuffs_base__pixel_subsampling pixel_subsampling() const;\n  inline wuffs_base__rect_ie_u32 bounds() const;\n  inline uint32_t width() const;\n  inline uint32_t height() const;\n  inline uint64_t pixbuf_len() const;\n#endif  // __cplusplus\n\n} wuffs_base__pixel_config;\n\nstatic inline wuffs_base__pixel_config  //\nwuffs_base__null_pixel_config() {\n  wuffs_base__pixel_config ret;\n  ret.private_impl.pix" +
+	"fmt = 0;\n  ret.private_impl.pixsub = 0;\n  ret.private_impl.width = 0;\n  ret.private_impl.height = 0;\n  return ret;\n}\n\n// TODO: Should this function return bool? An error type?\nstatic inline void  //\nwuffs_base__pixel_config__set(wuffs_base__pixel_config* c,\n                              wuffs_base__pixel_format pixfmt,\n                              wuffs_base__pixel_subsampling pixsub,\n                              uint32_t width,\n                              uint32_t height) {\n  if (!c) {\n    return;\n  }\n  if (pixfmt) {\n    uint64_t wh = ((uint64_t)width) * ((uint64_t)height);\n    // TODO: handle things other than 1 byte per pixel.\n    if (wh <= ((uint64_t)SIZE_MAX)) {\n      c->private_impl.pixfmt = pixfmt;\n      c->private_impl.pixsub = pixsub;\n      c->private_impl.width = width;\n      c->private_impl.height = height;\n      return;\n    }\n  }\n\n  c->private_impl.pixfmt = 0;\n  c->private_impl.pixsub = 0;\n  c->private_impl.width = 0;\n  c->private_impl.height = 0;\n}\n\nstatic inline void  //\nwuffs_base__pixel_co" +
+	"nfig__invalidate(wuffs_base__pixel_config* c) {\n  if (c) {\n    c->private_impl.pixfmt = 0;\n    c->private_impl.pixsub = 0;\n    c->private_impl.width = 0;\n    c->private_impl.height = 0;\n  }\n}\n\nstatic inline bool  //\nwuffs_base__pixel_config__is_valid(const wuffs_base__pixel_config* c) {\n  return c && c->private_impl.pixfmt;\n}\n\nstatic inline wuffs_base__pixel_format  //\nwuffs_base__pixel_config__pixel_format(const wuffs_base__pixel_config* c) {\n  return c ? c->private_impl.pixfmt : 0;\n}\n\nstatic inline wuffs_base__pixel_subsampling  //\nwuffs_base__pixel_config__pixel_subsampling(const wuffs_base__pixel_config* c) {\n  return c ? c->private_impl.pixsub : 0;\n}\n\nstatic inline wuffs_base__rect_ie_u32  //\nwuffs_base__pixel_config__bounds(const wuffs_base__pixel_config* c) {\n  if (c) {\n    wuffs_base__rect_ie_u32 ret;\n    ret.min_incl_x = 0;\n    ret.min_incl_y = 0;\n    ret.max_excl_x = c->private_impl.width;\n    ret.max_excl_y = c->private_impl.height;\n    return ret;\n  }\n\n  wuffs_base__rect_ie_u32 ret;\n  ret.min_incl" +
+	"_x = 0;\n  ret.min_incl_y = 0;\n  ret.max_excl_x = 0;\n  ret.max_excl_y = 0;\n  return ret;\n}\n\nstatic inline uint32_t  //\nwuffs_base__pixel_config__width(const wuffs_base__pixel_config* c) {\n  return c ? c->private_impl.width : 0;\n}\n\nstatic inline uint32_t  //\nwuffs_base__pixel_config__height(const wuffs_base__pixel_config* c) {\n  return c ? c->private_impl.height : 0;\n}\n\n// TODO: this is the right API for planar (not interleaved) pixbufs? Should it\n// allow decoding into a color model different from the format's intrinsic one?\n// For example, decoding a JPEG image straight to RGBA instead of to YCbCr?\nstatic inline uint64_t  //\nwuffs_base__pixel_config__pixbuf_len(const wuffs_base__pixel_config* c) {\n  if (!c) {\n    return 0;\n  }\n  if (wuffs_base__pixel_format__is_planar(c->private_impl.pixfmt)) {\n    // TODO: support planar pixel formats, concious of pixel subsampling.\n    return 0;\n  }\n  uint32_t bits_per_pixel =\n      wuffs_base__pixel_format__bits_per_pixel(c->private_impl.pixfmt);\n  if ((bits_per_pixel == 0" +
+	") || ((bits_per_pixel % 8) != 0)) {\n    // TODO: support fraction-of-byte pixels, e.g. 1 bit per pixel?\n    return 0;\n  }\n  uint64_t bytes_per_pixel = bits_per_pixel / 8;\n\n  uint64_t n =\n      ((uint64_t)c->private_impl.width) * ((uint64_t)c->private_impl.height);\n  if (n > (UINT64_MAX / bytes_per_pixel)) {\n    return 0;\n  }\n  n *= bytes_per_pixel;\n\n  if (wuffs_base__pixel_format__is_indexed(c->private_impl.pixfmt)) {\n    if (n > (UINT64_MAX - 1024)) {\n      return 0;\n    }\n    n += 1024;\n  }\n\n  return n;\n}\n\n#ifdef __cplusplus\n\ninline void  //\nwuffs_base__pixel_config::set(wuffs_base__pixel_format pixfmt,\n                              wuffs_base__pixel_subsampling pixsub,\n                              uint32_t width,\n                              uint32_t height) {\n  wuffs_base__pixel_config__set(this, pixfmt, pixsub, width, height);\n}\n\ninline void  //\nwuffs_base__pixel_config::invalidate() {\n  wuffs_base__pixel_config__invalidate(this);\n}\n\ninline bool  //\nwuffs_base__pixel_config::is_valid() const {\n  return" +
+	" wuffs_base__pixel_config__is_valid(this);\n}\n\ninline wuffs_base__pixel_format  //\nwuffs_base__pixel_config::pixel_format() const {\n  return wuffs_base__pixel_config__pixel_format(this);\n}\n\ninline wuffs_base__pixel_subsampling  //\nwuffs_base__pixel_config::pixel_subsampling() const {\n  return wuffs_base__pixel_config__pixel_subsampling(this);\n}\n\ninline wuffs_base__rect_ie_u32  //\nwuffs_base__pixel_config::bounds() const {\n  return wuffs_base__pixel_config__bounds(this);\n}\n\ninline uint32_t  //\nwuffs_base__pixel_config::width() const {\n  return wuffs_base__pixel_config__width(this);\n}\n\ninline uint32_t  //\nwuffs_base__pixel_config::height() const {\n  return wuffs_base__pixel_config__height(this);\n}\n\ninline uint64_t  //\nwuffs_base__pixel_config::pixbuf_len() const {\n  return wuffs_base__pixel_config__pixbuf_len(this);\n}\n\n#endif  // __cplusplus\n\n" +
 	"" +
-	"// --------\n\ntypedef struct {\n  wuffs_base__pixel_config pixcfg;\n\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    uint64_t first_frame_io_position;\n    bool first_frame_is_opaque;\n  } private_impl;\n\n#ifdef __cplusplus\n  inline void set(wuffs_base__pixel_format pixfmt,\n                  wuffs_base__pixel_subsampling pixsub,\n                  uint32_t width,\n                  uint32_t height,\n                  uint64_t first_frame_io_position,\n                  bool first_frame_is_opaque);\n  inline void invalidate();\n  inline bool is_valid();\n  inline uint64_t first_frame_io_position();\n  inline bool first_frame_is_opaque();\n#endif  // __cplusplus\n\n} wuffs_base__image_config;\n\nstatic inline wuffs_base__image_config  //\nwuffs_base__null_image_config() {\n  wuffs_base__image_config ret;\n  ret.pixcfg = wuffs_base__null_pixel_config();\n  ret.private_impl.first_frame_io_position = 0;\n  ret.private_impl.first_frame_is_opaque" +
-	" = false;\n  return ret;\n}\n\n// TODO: Should this function return bool? An error type?\nstatic inline void  //\nwuffs_base__image_config__set(wuffs_base__image_config* c,\n                              wuffs_base__pixel_format pixfmt,\n                              wuffs_base__pixel_subsampling pixsub,\n                              uint32_t width,\n                              uint32_t height,\n                              uint64_t first_frame_io_position,\n                              bool first_frame_is_opaque) {\n  if (!c) {\n    return;\n  }\n  if (wuffs_base__pixel_format__is_valid(pixfmt)) {\n    c->pixcfg.private_impl.pixfmt = pixfmt;\n    c->pixcfg.private_impl.pixsub = pixsub;\n    c->pixcfg.private_impl.width = width;\n    c->pixcfg.private_impl.height = height;\n    c->private_impl.first_frame_io_position = first_frame_io_position;\n    c->private_impl.first_frame_is_opaque = first_frame_is_opaque;\n    return;\n  }\n\n  c->pixcfg.private_impl.pixfmt = 0;\n  c->pixcfg.private_impl.pixsub = 0;\n  c->pixcfg.private_impl.w" +
-	"idth = 0;\n  c->pixcfg.private_impl.height = 0;\n  c->private_impl.first_frame_io_position = 0;\n  c->private_impl.first_frame_is_opaque = 0;\n}\n\nstatic inline void  //\nwuffs_base__image_config__invalidate(wuffs_base__image_config* c) {\n  if (c) {\n    c->pixcfg.private_impl.pixfmt = 0;\n    c->pixcfg.private_impl.pixsub = 0;\n    c->pixcfg.private_impl.width = 0;\n    c->pixcfg.private_impl.height = 0;\n    c->private_impl.first_frame_io_position = 0;\n    c->private_impl.first_frame_is_opaque = 0;\n  }\n}\n\nstatic inline bool  //\nwuffs_base__image_config__is_valid(wuffs_base__image_config* c) {\n  return c && wuffs_base__pixel_config__is_valid(&(c->pixcfg));\n}\n\nstatic inline uint64_t  //\nwuffs_base__image_config__first_frame_io_position(wuffs_base__image_config* c) {\n  return c ? c->private_impl.first_frame_io_position : 0;\n}\n\nstatic inline bool  //\nwuffs_base__image_config__first_frame_is_opaque(wuffs_base__image_config* c) {\n  return c ? c->private_impl.first_frame_is_opaque : false;\n}\n\n#ifdef __cplusplus\n\ninline void " +
-	" //\nwuffs_base__image_config::set(wuffs_base__pixel_format pixfmt,\n                              wuffs_base__pixel_subsampling pixsub,\n                              uint32_t width,\n                              uint32_t height,\n                              uint64_t first_frame_io_position,\n                              bool first_frame_is_opaque) {\n  wuffs_base__image_config__set(this, pixfmt, pixsub, width, height,\n                                first_frame_io_position, first_frame_is_opaque);\n}\n\ninline void  //\nwuffs_base__image_config::invalidate() {\n  wuffs_base__image_config__invalidate(this);\n}\n\ninline bool  //\nwuffs_base__image_config::is_valid() {\n  return wuffs_base__image_config__is_valid(this);\n}\n\ninline uint64_t  //\nwuffs_base__image_config::first_frame_io_position() {\n  return wuffs_base__image_config__first_frame_io_position(this);\n}\n\ninline bool  //\nwuffs_base__image_config::first_frame_is_opaque() {\n  return wuffs_base__image_config__first_frame_is_opaque(this);\n}\n\n#endif  // __cplusplus\n\n" +
+	"// --------\n\ntypedef struct {\n  wuffs_base__pixel_config pixcfg;\n\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    uint64_t first_frame_io_position;\n    bool first_frame_is_opaque;\n  } private_impl;\n\n#ifdef __cplusplus\n  inline void set(wuffs_base__pixel_format pixfmt,\n                  wuffs_base__pixel_subsampling pixsub,\n                  uint32_t width,\n                  uint32_t height,\n                  uint64_t first_frame_io_position,\n                  bool first_frame_is_opaque);\n  inline void invalidate();\n  inline bool is_valid() const;\n  inline uint64_t first_frame_io_position() const;\n  inline bool first_frame_is_opaque() const;\n#endif  // __cplusplus\n\n} wuffs_base__image_config;\n\nstatic inline wuffs_base__image_config  //\nwuffs_base__null_image_config() {\n  wuffs_base__image_config ret;\n  ret.pixcfg = wuffs_base__null_pixel_config();\n  ret.private_impl.first_frame_io_position = 0;\n  ret.private_impl.fir" +
+	"st_frame_is_opaque = false;\n  return ret;\n}\n\n// TODO: Should this function return bool? An error type?\nstatic inline void  //\nwuffs_base__image_config__set(wuffs_base__image_config* c,\n                              wuffs_base__pixel_format pixfmt,\n                              wuffs_base__pixel_subsampling pixsub,\n                              uint32_t width,\n                              uint32_t height,\n                              uint64_t first_frame_io_position,\n                              bool first_frame_is_opaque) {\n  if (!c) {\n    return;\n  }\n  if (wuffs_base__pixel_format__is_valid(pixfmt)) {\n    c->pixcfg.private_impl.pixfmt = pixfmt;\n    c->pixcfg.private_impl.pixsub = pixsub;\n    c->pixcfg.private_impl.width = width;\n    c->pixcfg.private_impl.height = height;\n    c->private_impl.first_frame_io_position = first_frame_io_position;\n    c->private_impl.first_frame_is_opaque = first_frame_is_opaque;\n    return;\n  }\n\n  c->pixcfg.private_impl.pixfmt = 0;\n  c->pixcfg.private_impl.pixsub = 0;\n  c->pix" +
+	"cfg.private_impl.width = 0;\n  c->pixcfg.private_impl.height = 0;\n  c->private_impl.first_frame_io_position = 0;\n  c->private_impl.first_frame_is_opaque = 0;\n}\n\nstatic inline void  //\nwuffs_base__image_config__invalidate(wuffs_base__image_config* c) {\n  if (c) {\n    c->pixcfg.private_impl.pixfmt = 0;\n    c->pixcfg.private_impl.pixsub = 0;\n    c->pixcfg.private_impl.width = 0;\n    c->pixcfg.private_impl.height = 0;\n    c->private_impl.first_frame_io_position = 0;\n    c->private_impl.first_frame_is_opaque = 0;\n  }\n}\n\nstatic inline bool  //\nwuffs_base__image_config__is_valid(const wuffs_base__image_config* c) {\n  return c && wuffs_base__pixel_config__is_valid(&(c->pixcfg));\n}\n\nstatic inline uint64_t  //\nwuffs_base__image_config__first_frame_io_position(\n    const wuffs_base__image_config* c) {\n  return c ? c->private_impl.first_frame_io_position : 0;\n}\n\nstatic inline bool  //\nwuffs_base__image_config__first_frame_is_opaque(\n    const wuffs_base__image_config* c) {\n  return c ? c->private_impl.first_frame_is_opaqu" +
+	"e : false;\n}\n\n#ifdef __cplusplus\n\ninline void  //\nwuffs_base__image_config::set(wuffs_base__pixel_format pixfmt,\n                              wuffs_base__pixel_subsampling pixsub,\n                              uint32_t width,\n                              uint32_t height,\n                              uint64_t first_frame_io_position,\n                              bool first_frame_is_opaque) {\n  wuffs_base__image_config__set(this, pixfmt, pixsub, width, height,\n                                first_frame_io_position, first_frame_is_opaque);\n}\n\ninline void  //\nwuffs_base__image_config::invalidate() {\n  wuffs_base__image_config__invalidate(this);\n}\n\ninline bool  //\nwuffs_base__image_config::is_valid() const {\n  return wuffs_base__image_config__is_valid(this);\n}\n\ninline uint64_t  //\nwuffs_base__image_config::first_frame_io_position() const {\n  return wuffs_base__image_config__first_frame_io_position(this);\n}\n\ninline bool  //\nwuffs_base__image_config::first_frame_is_opaque() const {\n  return wuffs_base__image_co" +
+	"nfig__first_frame_is_opaque(this);\n}\n\n#endif  // __cplusplus\n\n" +
 	"" +
 	"// --------\n\n// wuffs_base__animation_blend encodes, for an animated image, how to blend the\n// transparent pixels of this frame with the existing canvas. In Porter-Duff\n// compositing operator terminology:\n//  - 0 means the frame may be transparent, and should be blended \"src over\n//    dst\", also known as just \"over\".\n//  - 1 means the frame may be transparent, and should be blended \"src\".\n//  - 2 means the frame is completely opaque, so that \"src over dst\" and \"src\"\n//    are equivalent.\n//\n// These semantics are conservative. It is valid for a completely opaque frame\n// to have a blend value other than 2.\ntypedef uint8_t wuffs_base__animation_blend;\n\n#define WUFFS_BASE__ANIMATION_BLEND__SRC_OVER_DST \\\n  ((wuffs_base__animation_blend)0)\n#define WUFFS_BASE__ANIMATION_BLEND__SRC ((wuffs_base__animation_blend)1)\n#define WUFFS_BASE__ANIMATION_BLEND__OPAQUE ((wuffs_base__animation_blend)2)\n\n" +
 	"" +
 	"// --------\n\n// wuffs_base__animation_disposal encodes, for an animated image, how to\n// dispose of a frame after displaying it:\n//  - None means to draw the next frame on top of this one.\n//  - Restore Background means to clear the frame's dirty rectangle to \"the\n//    background color\" (in practice, this means transparent black) before\n//    drawing the next frame.\n//  - Restore Previous means to undo the current frame, so that the next frame\n//    is drawn on top of the previous one.\ntypedef uint8_t wuffs_base__animation_disposal;\n\n#define WUFFS_BASE__ANIMATION_DISPOSAL__NONE ((wuffs_base__animation_disposal)0)\n#define WUFFS_BASE__ANIMATION_DISPOSAL__RESTORE_BACKGROUND \\\n  ((wuffs_base__animation_disposal)1)\n#define WUFFS_BASE__ANIMATION_DISPOSAL__RESTORE_PREVIOUS \\\n  ((wuffs_base__animation_disposal)2)\n\n" +
 	"" +
-	"// --------\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__rect_ie_u32 bounds;\n    wuffs_base__flicks duration;\n    uint64_t index;\n    uint64_t io_position;\n    wuffs_base__animation_blend blend;\n    wuffs_base__animation_disposal disposal;\n  } private_impl;\n\n#ifdef __cplusplus\n  inline void update(wuffs_base__rect_ie_u32 bounds,\n                     wuffs_base__flicks duration,\n                     uint64_t index,\n                     uint64_t io_position,\n                     wuffs_base__animation_blend blend,\n                     wuffs_base__animation_disposal disposal);\n  inline wuffs_base__rect_ie_u32 bounds();\n  inline uint32_t width();\n  inline uint32_t height();\n  inline wuffs_base__flicks duration();\n  inline uint64_t index();\n  inline uint64_t io_position();\n  inline wuffs_base__animation_blend blend();\n  inline wuffs_base__animation_disposal disposal();\n#endif  // __cpluspl" +
-	"us\n\n} wuffs_base__frame_config;\n\nstatic inline wuffs_base__frame_config  //\nwuffs_base__null_frame_config() {\n  wuffs_base__frame_config ret;\n  ret.private_impl.bounds = wuffs_base__make_rect_ie_u32(0, 0, 0, 0);\n  ret.private_impl.duration = 0;\n  ret.private_impl.index = 0;\n  ret.private_impl.io_position = 0;\n  ret.private_impl.blend = 0;\n  ret.private_impl.disposal = 0;\n  return ret;\n}\n\nstatic inline void  //\nwuffs_base__frame_config__update(wuffs_base__frame_config* c,\n                                 wuffs_base__rect_ie_u32 bounds,\n                                 wuffs_base__flicks duration,\n                                 uint64_t index,\n                                 uint64_t io_position,\n                                 wuffs_base__animation_blend blend,\n                                 wuffs_base__animation_disposal disposal) {\n  if (!c) {\n    return;\n  }\n\n  c->private_impl.bounds = bounds;\n  c->private_impl.duration = duration;\n  c->private_impl.index = index;\n  c->private_impl.io_position = io_po" +
-	"sition;\n  c->private_impl.blend = blend;\n  c->private_impl.disposal = disposal;\n}\n\nstatic inline wuffs_base__rect_ie_u32  //\nwuffs_base__frame_config__bounds(wuffs_base__frame_config* c) {\n  if (c) {\n    return c->private_impl.bounds;\n  }\n\n  wuffs_base__rect_ie_u32 ret;\n  ret.min_incl_x = 0;\n  ret.min_incl_y = 0;\n  ret.max_excl_x = 0;\n  ret.max_excl_y = 0;\n  return ret;\n}\n\nstatic inline uint32_t  //\nwuffs_base__frame_config__width(wuffs_base__frame_config* c) {\n  return c ? wuffs_base__rect_ie_u32__width(&c->private_impl.bounds) : 0;\n}\n\nstatic inline uint32_t  //\nwuffs_base__frame_config__height(wuffs_base__frame_config* c) {\n  return c ? wuffs_base__rect_ie_u32__height(&c->private_impl.bounds) : 0;\n}\n\n// wuffs_base__frame_config__duration returns the amount of time to display\n// this frame. Zero means to display forever - a still (non-animated) image.\nstatic inline wuffs_base__flicks  //\nwuffs_base__frame_config__duration(wuffs_base__frame_config* c) {\n  return c ? c->private_impl.duration : 0;\n}\n\n// wuffs_b" +
-	"ase__frame_config__index returns the index of this frame. The first\n// frame in an image has index 0, the second frame has index 1, and so on.\nstatic inline uint64_t  //\nwuffs_base__frame_config__index(wuffs_base__frame_config* c) {\n  return c ? c->private_impl.index : 0;\n}\n\n// wuffs_base__frame_config__io_position returns the I/O stream position before\n// the frame config.\nstatic inline uint64_t  //\nwuffs_base__frame_config__io_position(wuffs_base__frame_config* c) {\n  return c ? c->private_impl.io_position : 0;\n}\n\n// wuffs_base__frame_config__blend returns, for an animated image, how to blend\n// the transparent pixels of this frame with the existing canvas.\nstatic inline wuffs_base__animation_blend  //\nwuffs_base__frame_config__blend(wuffs_base__frame_config* c) {\n  return c ? c->private_impl.blend : 0;\n}\n\n// wuffs_base__frame_config__disposal returns, for an animated image, how to\n// dispose of this frame after displaying it.\nstatic inline wuffs_base__animation_disposal  //\nwuffs_base__frame_config__dispos" +
-	"al(wuffs_base__frame_config* c) {\n  return c ? c->private_impl.disposal : 0;\n}\n\n#ifdef __cplusplus\n\ninline void  //\nwuffs_base__frame_config::update(wuffs_base__rect_ie_u32 bounds,\n                                 wuffs_base__flicks duration,\n                                 uint64_t index,\n                                 uint64_t io_position,\n                                 wuffs_base__animation_blend blend,\n                                 wuffs_base__animation_disposal disposal) {\n  wuffs_base__frame_config__update(this, bounds, duration, index, io_position,\n                                   blend, disposal);\n}\n\ninline wuffs_base__rect_ie_u32  //\nwuffs_base__frame_config::bounds() {\n  return wuffs_base__frame_config__bounds(this);\n}\n\ninline uint32_t  //\nwuffs_base__frame_config::width() {\n  return wuffs_base__frame_config__width(this);\n}\n\ninline uint32_t  //\nwuffs_base__frame_config::height() {\n  return wuffs_base__frame_config__height(this);\n}\n\ninline wuffs_base__flicks  //\nwuffs_base__frame_config::du" +
-	"ration() {\n  return wuffs_base__frame_config__duration(this);\n}\n\ninline uint64_t  //\nwuffs_base__frame_config::index() {\n  return wuffs_base__frame_config__index(this);\n}\n\ninline uint64_t  //\nwuffs_base__frame_config::io_position() {\n  return wuffs_base__frame_config__io_position(this);\n}\n\ninline wuffs_base__animation_blend  //\nwuffs_base__frame_config::blend() {\n  return wuffs_base__frame_config__blend(this);\n}\n\ninline wuffs_base__animation_disposal  //\nwuffs_base__frame_config::disposal() {\n  return wuffs_base__frame_config__disposal(this);\n}\n\n#endif  // __cplusplus\n\n" +
+	"// --------\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__rect_ie_u32 bounds;\n    wuffs_base__flicks duration;\n    uint64_t index;\n    uint64_t io_position;\n    wuffs_base__animation_blend blend;\n    wuffs_base__animation_disposal disposal;\n    wuffs_base__color_u32_argb_premul background_color;\n  } private_impl;\n\n#ifdef __cplusplus\n  inline void update(wuffs_base__rect_ie_u32 bounds,\n                     wuffs_base__flicks duration,\n                     uint64_t index,\n                     uint64_t io_position,\n                     wuffs_base__animation_blend blend,\n                     wuffs_base__animation_disposal disposal,\n                     wuffs_base__color_u32_argb_premul background_color);\n  inline wuffs_base__rect_ie_u32 bounds() const;\n  inline uint32_t width() const;\n  inline uint32_t height() const;\n  inline wuffs_base__flicks duration() const;\n  inline uint64_t index()" +
+	" const;\n  inline uint64_t io_position() const;\n  inline wuffs_base__animation_blend blend() const;\n  inline wuffs_base__animation_disposal disposal() const;\n  inline wuffs_base__color_u32_argb_premul background_color() const;\n#endif  // __cplusplus\n\n} wuffs_base__frame_config;\n\nstatic inline wuffs_base__frame_config  //\nwuffs_base__null_frame_config() {\n  wuffs_base__frame_config ret;\n  ret.private_impl.bounds = wuffs_base__make_rect_ie_u32(0, 0, 0, 0);\n  ret.private_impl.duration = 0;\n  ret.private_impl.index = 0;\n  ret.private_impl.io_position = 0;\n  ret.private_impl.blend = 0;\n  ret.private_impl.disposal = 0;\n  return ret;\n}\n\nstatic inline void  //\nwuffs_base__frame_config__update(\n    wuffs_base__frame_config* c,\n    wuffs_base__rect_ie_u32 bounds,\n    wuffs_base__flicks duration,\n    uint64_t index,\n    uint64_t io_position,\n    wuffs_base__animation_blend blend,\n    wuffs_base__animation_disposal disposal,\n    wuffs_base__color_u32_argb_premul background_color) {\n  if (!c) {\n    return;\n  }\n\n  c->privat" +
+	"e_impl.bounds = bounds;\n  c->private_impl.duration = duration;\n  c->private_impl.index = index;\n  c->private_impl.io_position = io_position;\n  c->private_impl.blend = blend;\n  c->private_impl.disposal = disposal;\n  c->private_impl.background_color = background_color;\n}\n\nstatic inline wuffs_base__rect_ie_u32  //\nwuffs_base__frame_config__bounds(const wuffs_base__frame_config* c) {\n  if (c) {\n    return c->private_impl.bounds;\n  }\n\n  wuffs_base__rect_ie_u32 ret;\n  ret.min_incl_x = 0;\n  ret.min_incl_y = 0;\n  ret.max_excl_x = 0;\n  ret.max_excl_y = 0;\n  return ret;\n}\n\nstatic inline uint32_t  //\nwuffs_base__frame_config__width(const wuffs_base__frame_config* c) {\n  return c ? wuffs_base__rect_ie_u32__width(&c->private_impl.bounds) : 0;\n}\n\nstatic inline uint32_t  //\nwuffs_base__frame_config__height(const wuffs_base__frame_config* c) {\n  return c ? wuffs_base__rect_ie_u32__height(&c->private_impl.bounds) : 0;\n}\n\n// wuffs_base__frame_config__duration returns the amount of time to display\n// this frame. Zero means to d" +
+	"isplay forever - a still (non-animated) image.\nstatic inline wuffs_base__flicks  //\nwuffs_base__frame_config__duration(const wuffs_base__frame_config* c) {\n  return c ? c->private_impl.duration : 0;\n}\n\n// wuffs_base__frame_config__index returns the index of this frame. The first\n// frame in an image has index 0, the second frame has index 1, and so on.\nstatic inline uint64_t  //\nwuffs_base__frame_config__index(const wuffs_base__frame_config* c) {\n  return c ? c->private_impl.index : 0;\n}\n\n// wuffs_base__frame_config__io_position returns the I/O stream position before\n// the frame config.\nstatic inline uint64_t  //\nwuffs_base__frame_config__io_position(const wuffs_base__frame_config* c) {\n  return c ? c->private_impl.io_position : 0;\n}\n\n// wuffs_base__frame_config__blend returns, for an animated image, how to blend\n// the transparent pixels of this frame with the existing canvas.\nstatic inline wuffs_base__animation_blend  //\nwuffs_base__frame_config__blend(const wuffs_base__frame_config* c) {\n  return c ? c->p" +
+	"rivate_impl.blend : 0;\n}\n\n// wuffs_base__frame_config__disposal returns, for an animated image, how to\n// dispose of this frame after displaying it.\nstatic inline wuffs_base__animation_disposal  //\nwuffs_base__frame_config__disposal(const wuffs_base__frame_config* c) {\n  return c ? c->private_impl.disposal : 0;\n}\n\nstatic inline wuffs_base__color_u32_argb_premul  //\nwuffs_base__frame_config__background_color(const wuffs_base__frame_config* c) {\n  return c ? c->private_impl.background_color : 0;\n}\n\n#ifdef __cplusplus\n\ninline void  //\nwuffs_base__frame_config::update(\n    wuffs_base__rect_ie_u32 bounds,\n    wuffs_base__flicks duration,\n    uint64_t index,\n    uint64_t io_position,\n    wuffs_base__animation_blend blend,\n    wuffs_base__animation_disposal disposal,\n    wuffs_base__color_u32_argb_premul background_color) {\n  wuffs_base__frame_config__update(this, bounds, duration, index, io_position,\n                                   blend, disposal, background_color);\n}\n\ninline wuffs_base__rect_ie_u32  //\nwuffs_b" +
+	"ase__frame_config::bounds() const {\n  return wuffs_base__frame_config__bounds(this);\n}\n\ninline uint32_t  //\nwuffs_base__frame_config::width() const {\n  return wuffs_base__frame_config__width(this);\n}\n\ninline uint32_t  //\nwuffs_base__frame_config::height() const {\n  return wuffs_base__frame_config__height(this);\n}\n\ninline wuffs_base__flicks  //\nwuffs_base__frame_config::duration() const {\n  return wuffs_base__frame_config__duration(this);\n}\n\ninline uint64_t  //\nwuffs_base__frame_config::index() const {\n  return wuffs_base__frame_config__index(this);\n}\n\ninline uint64_t  //\nwuffs_base__frame_config::io_position() const {\n  return wuffs_base__frame_config__io_position(this);\n}\n\ninline wuffs_base__animation_blend  //\nwuffs_base__frame_config::blend() const {\n  return wuffs_base__frame_config__blend(this);\n}\n\ninline wuffs_base__animation_disposal  //\nwuffs_base__frame_config::disposal() const {\n  return wuffs_base__frame_config__disposal(this);\n}\n\ninline wuffs_base__color_u32_argb_premul  //\nwuffs_base__frame_confi" +
+	"g::background_color() const {\n  return wuffs_base__frame_config__background_color(this);\n}\n\n#endif  // __cplusplus\n\n" +
 	"" +
-	"// --------\n\ntypedef struct {\n  wuffs_base__pixel_config pixcfg;\n\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__table_u8 planes[WUFFS_BASE__PIXEL_FORMAT__NUM_PLANES_MAX];\n    // TODO: color spaces.\n  } private_impl;\n\n#ifdef __cplusplus\n  inline wuffs_base__status set_from_slice(wuffs_base__pixel_config* pixcfg,\n                                           wuffs_base__slice_u8 pixbuf_memory);\n  inline wuffs_base__slice_u8 palette();\n  inline wuffs_base__pixel_format pixel_format();\n  inline wuffs_base__table_u8 plane(uint32_t p);\n#endif  // __cplusplus\n\n} wuffs_base__pixel_buffer;\n\nstatic inline wuffs_base__pixel_buffer  //\nwuffs_base__null_pixel_buffer() {\n  wuffs_base__pixel_buffer ret;\n  ret.pixcfg = wuffs_base__null_pixel_config();\n  ret.private_impl.planes[0] = wuffs_base__null_table_u8();\n  ret.private_impl.planes[1] = wuffs_base__null_table_u8();\n  ret.private_impl.planes[2] = wuffs_base__null_tabl" +
-	"e_u8();\n  ret.private_impl.planes[3] = wuffs_base__null_table_u8();\n  return ret;\n}\n\nstatic inline wuffs_base__status  //\nwuffs_base__pixel_buffer__set_from_slice(wuffs_base__pixel_buffer* b,\n                                         wuffs_base__pixel_config* pixcfg,\n                                         wuffs_base__slice_u8 pixbuf_memory) {\n  if (!b) {\n    return wuffs_base__error__bad_receiver;\n  }\n  memset(b, 0, sizeof(*b));\n  if (!pixcfg) {\n    return wuffs_base__error__bad_argument;\n  }\n  if (wuffs_base__pixel_format__is_planar(pixcfg->private_impl.pixfmt)) {\n    // TODO: support planar pixel formats, concious of pixel subsampling.\n    return wuffs_base__error__bad_argument;\n  }\n  uint32_t bits_per_pixel =\n      wuffs_base__pixel_format__bits_per_pixel(pixcfg->private_impl.pixfmt);\n  if ((bits_per_pixel == 0) || ((bits_per_pixel % 8) != 0)) {\n    return wuffs_base__error__bad_argument;\n  }\n  uint64_t bytes_per_pixel = bits_per_pixel / 8;\n\n  uint8_t* ptr = pixbuf_memory.ptr;\n  uint64_t len = pixbuf_memo" +
-	"ry.len;\n  if (wuffs_base__pixel_format__is_indexed(pixcfg->private_impl.pixfmt)) {\n    // Split a 1024 byte chunk (256 palette entries × 4 bytes per entry) from\n    // the start of pixbuf_memory. We split from the start, not the end, so\n    // that the both chunks' pointers have the same alignment as the original\n    // pointer, up to an alignment of 1024.\n    if (len < 1024) {\n      return wuffs_base__error__bad_argument_length_too_short;\n    }\n    wuffs_base__table_u8* tab =\n        &b->private_impl.planes[WUFFS_BASE__PIXEL_FORMAT__INDEXED__COLOR_PLANE];\n    tab->ptr = ptr;\n    tab->width = 1024;\n    tab->height = 1;\n    tab->stride = 1024;\n    ptr += 1024;\n    len -= 1024;\n  }\n\n  uint64_t wh = ((uint64_t)pixcfg->private_impl.width) *\n                ((uint64_t)pixcfg->private_impl.height);\n  size_t width = (size_t)(pixcfg->private_impl.width);\n  if ((wh > (UINT64_MAX / bytes_per_pixel)) ||\n      (width > (SIZE_MAX / bytes_per_pixel))) {\n    return wuffs_base__error__bad_argument;\n  }\n  wh *= bytes_per_pix" +
-	"el;\n  width *= bytes_per_pixel;\n  if (wh > len) {\n    return wuffs_base__error__bad_argument_length_too_short;\n  }\n\n  b->pixcfg = *pixcfg;\n  wuffs_base__table_u8* tab = &b->private_impl.planes[0];\n  tab->ptr = ptr;\n  tab->width = width;\n  tab->height = pixcfg->private_impl.height;\n  tab->stride = width;\n  return NULL;\n}\n\n// wuffs_base__pixel_buffer__palette returns the palette color data. If\n// non-empty, it will have length 1024.\nstatic inline wuffs_base__slice_u8  //\nwuffs_base__pixel_buffer__palette(wuffs_base__pixel_buffer* b) {\n  if (b &&\n      wuffs_base__pixel_format__is_indexed(b->pixcfg.private_impl.pixfmt)) {\n    wuffs_base__table_u8* tab =\n        &b->private_impl.planes[WUFFS_BASE__PIXEL_FORMAT__INDEXED__COLOR_PLANE];\n    if ((tab->width == 1024) && (tab->height == 1)) {\n      return wuffs_base__make_slice_u8(tab->ptr, 1024);\n    }\n  }\n  return wuffs_base__make_slice_u8(NULL, 0);\n}\n\nstatic inline wuffs_base__pixel_format  //\nwuffs_base__pixel_buffer__pixel_format(wuffs_base__pixel_buffer* b) {\n  i" +
-	"f (b) {\n    return b->pixcfg.private_impl.pixfmt;\n  }\n  return WUFFS_BASE__PIXEL_FORMAT__INVALID;\n}\n\nstatic inline wuffs_base__table_u8  //\nwuffs_base__pixel_buffer__plane(wuffs_base__pixel_buffer* b, uint32_t p) {\n  if (b && (p < WUFFS_BASE__PIXEL_FORMAT__NUM_PLANES_MAX)) {\n    return b->private_impl.planes[p];\n  }\n\n  wuffs_base__table_u8 ret;\n  ret.ptr = NULL;\n  ret.width = 0;\n  ret.height = 0;\n  ret.stride = 0;\n  return ret;\n}\n\n#ifdef __cplusplus\n\ninline wuffs_base__status  //\nwuffs_base__pixel_buffer::set_from_slice(wuffs_base__pixel_config* pixcfg,\n                                         wuffs_base__slice_u8 pixbuf_memory) {\n  return wuffs_base__pixel_buffer__set_from_slice(this, pixcfg, pixbuf_memory);\n}\n\ninline wuffs_base__slice_u8  //\nwuffs_base__pixel_buffer::palette() {\n  return wuffs_base__pixel_buffer__palette(this);\n}\n\ninline wuffs_base__pixel_format  //\nwuffs_base__pixel_buffer::pixel_format() {\n  return wuffs_base__pixel_buffer__pixel_format(this);\n}\n\ninline wuffs_base__table_u8  //\nwuffs_base" +
-	"__pixel_buffer::plane(uint32_t p) {\n  return wuffs_base__pixel_buffer__plane(this, p);\n}\n\n#endif  // __cplusplus\n\n" +
+	"// --------\n\ntypedef struct {\n  wuffs_base__pixel_config pixcfg;\n\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__table_u8 planes[WUFFS_BASE__PIXEL_FORMAT__NUM_PLANES_MAX];\n    // TODO: color spaces.\n  } private_impl;\n\n#ifdef __cplusplus\n  inline wuffs_base__status set_from_slice(wuffs_base__pixel_config* pixcfg,\n                                           wuffs_base__slice_u8 pixbuf_memory);\n  inline wuffs_base__status set_from_table(wuffs_base__pixel_config* pixcfg,\n                                           wuffs_base__table_u8 pixbuf_memory);\n  inline wuffs_base__slice_u8 palette();\n  inline wuffs_base__pixel_format pixel_format() const;\n  inline wuffs_base__table_u8 plane(uint32_t p);\n#endif  // __cplusplus\n\n} wuffs_base__pixel_buffer;\n\nstatic inline wuffs_base__pixel_buffer  //\nwuffs_base__null_pixel_buffer() {\n  wuffs_base__pixel_buffer ret;\n  ret.pixcfg = wuffs_base__null_pixel_config();\n  ret.pri" +
+	"vate_impl.planes[0] = wuffs_base__null_table_u8();\n  ret.private_impl.planes[1] = wuffs_base__null_table_u8();\n  ret.private_impl.planes[2] = wuffs_base__null_table_u8();\n  ret.private_impl.planes[3] = wuffs_base__null_table_u8();\n  return ret;\n}\n\nstatic inline wuffs_base__status  //\nwuffs_base__pixel_buffer__set_from_slice(wuffs_base__pixel_buffer* b,\n                                         wuffs_base__pixel_config* pixcfg,\n                                         wuffs_base__slice_u8 pixbuf_memory) {\n  if (!b) {\n    return wuffs_base__error__bad_receiver;\n  }\n  memset(b, 0, sizeof(*b));\n  if (!pixcfg) {\n    return wuffs_base__error__bad_argument;\n  }\n  if (wuffs_base__pixel_format__is_planar(pixcfg->private_impl.pixfmt)) {\n    // TODO: support planar pixel formats, concious of pixel subsampling.\n    return wuffs_base__error__unsupported_option;\n  }\n  uint32_t bits_per_pixel =\n      wuffs_base__pixel_format__bits_per_pixel(pixcfg->private_impl.pixfmt);\n  if ((bits_per_pixel == 0) || ((bits_per_pixel % 8) !=" +
+	" 0)) {\n    // TODO: support fraction-of-byte pixels, e.g. 1 bit per pixel?\n    return wuffs_base__error__unsupported_option;\n  }\n  uint64_t bytes_per_pixel = bits_per_pixel / 8;\n\n  uint8_t* ptr = pixbuf_memory.ptr;\n  uint64_t len = pixbuf_memory.len;\n  if (wuffs_base__pixel_format__is_indexed(pixcfg->private_impl.pixfmt)) {\n    // Split a 1024 byte chunk (256 palette entries × 4 bytes per entry) from\n    // the start of pixbuf_memory. We split from the start, not the end, so\n    // that the both chunks' pointers have the same alignment as the original\n    // pointer, up to an alignment of 1024.\n    if (len < 1024) {\n      return wuffs_base__error__bad_argument_length_too_short;\n    }\n    wuffs_base__table_u8* tab =\n        &b->private_impl.planes[WUFFS_BASE__PIXEL_FORMAT__INDEXED__COLOR_PLANE];\n    tab->ptr = ptr;\n    tab->width = 1024;\n    tab->height = 1;\n    tab->stride = 1024;\n    ptr += 1024;\n    len -= 1024;\n  }\n\n  uint64_t wh = ((uint64_t)pixcfg->private_impl.width) *\n                ((uint64_t)pixcfg" +
+	"->private_impl.height);\n  size_t width = (size_t)(pixcfg->private_impl.width);\n  if ((wh > (UINT64_MAX / bytes_per_pixel)) ||\n      (width > (SIZE_MAX / bytes_per_pixel))) {\n    return wuffs_base__error__bad_argument;\n  }\n  wh *= bytes_per_pixel;\n  width *= bytes_per_pixel;\n  if (wh > len) {\n    return wuffs_base__error__bad_argument_length_too_short;\n  }\n\n  b->pixcfg = *pixcfg;\n  wuffs_base__table_u8* tab = &b->private_impl.planes[0];\n  tab->ptr = ptr;\n  tab->width = width;\n  tab->height = pixcfg->private_impl.height;\n  tab->stride = width;\n  return NULL;\n}\n\nstatic inline wuffs_base__status  //\nwuffs_base__pixel_buffer__set_from_table(wuffs_base__pixel_buffer* b,\n                                         wuffs_base__pixel_config* pixcfg,\n                                         wuffs_base__table_u8 pixbuf_memory) {\n  if (!b) {\n    return wuffs_base__error__bad_receiver;\n  }\n  memset(b, 0, sizeof(*b));\n  if (!pixcfg ||\n      wuffs_base__pixel_format__is_planar(pixcfg->private_impl.pixfmt)) {\n    return wuffs_b" +
+	"ase__error__bad_argument;\n  }\n  uint32_t bits_per_pixel =\n      wuffs_base__pixel_format__bits_per_pixel(pixcfg->private_impl.pixfmt);\n  if ((bits_per_pixel == 0) || ((bits_per_pixel % 8) != 0)) {\n    // TODO: support fraction-of-byte pixels, e.g. 1 bit per pixel?\n    return wuffs_base__error__unsupported_option;\n  }\n  uint64_t bytes_per_pixel = bits_per_pixel / 8;\n\n  uint64_t width_in_bytes =\n      ((uint64_t)pixcfg->private_impl.width) * bytes_per_pixel;\n  if ((width_in_bytes > pixbuf_memory.width) ||\n      (pixcfg->private_impl.height > pixbuf_memory.height)) {\n    return wuffs_base__error__bad_argument;\n  }\n\n  b->pixcfg = *pixcfg;\n  b->private_impl.planes[0] = pixbuf_memory;\n  return NULL;\n}\n\n// wuffs_base__pixel_buffer__palette returns the palette color data. If\n// non-empty, it will have length 1024.\nstatic inline wuffs_base__slice_u8  //\nwuffs_base__pixel_buffer__palette(wuffs_base__pixel_buffer* b) {\n  if (b &&\n      wuffs_base__pixel_format__is_indexed(b->pixcfg.private_impl.pixfmt)) {\n    wuffs_base" +
+	"__table_u8* tab =\n        &b->private_impl.planes[WUFFS_BASE__PIXEL_FORMAT__INDEXED__COLOR_PLANE];\n    if ((tab->width == 1024) && (tab->height == 1)) {\n      return wuffs_base__make_slice_u8(tab->ptr, 1024);\n    }\n  }\n  return wuffs_base__make_slice_u8(NULL, 0);\n}\n\nstatic inline wuffs_base__pixel_format  //\nwuffs_base__pixel_buffer__pixel_format(const wuffs_base__pixel_buffer* b) {\n  if (b) {\n    return b->pixcfg.private_impl.pixfmt;\n  }\n  return WUFFS_BASE__PIXEL_FORMAT__INVALID;\n}\n\nstatic inline wuffs_base__table_u8  //\nwuffs_base__pixel_buffer__plane(wuffs_base__pixel_buffer* b, uint32_t p) {\n  if (b && (p < WUFFS_BASE__PIXEL_FORMAT__NUM_PLANES_MAX)) {\n    return b->private_impl.planes[p];\n  }\n\n  wuffs_base__table_u8 ret;\n  ret.ptr = NULL;\n  ret.width = 0;\n  ret.height = 0;\n  ret.stride = 0;\n  return ret;\n}\n\n#ifdef __cplusplus\n\ninline wuffs_base__status  //\nwuffs_base__pixel_buffer::set_from_slice(wuffs_base__pixel_config* pixcfg,\n                                         wuffs_base__slice_u8 pixbuf_memory" +
+	") {\n  return wuffs_base__pixel_buffer__set_from_slice(this, pixcfg, pixbuf_memory);\n}\n\ninline wuffs_base__status  //\nwuffs_base__pixel_buffer::set_from_table(wuffs_base__pixel_config* pixcfg,\n                                         wuffs_base__table_u8 pixbuf_memory) {\n  return wuffs_base__pixel_buffer__set_from_table(this, pixcfg, pixbuf_memory);\n}\n\ninline wuffs_base__slice_u8  //\nwuffs_base__pixel_buffer::palette() {\n  return wuffs_base__pixel_buffer__palette(this);\n}\n\ninline wuffs_base__pixel_format  //\nwuffs_base__pixel_buffer::pixel_format() const {\n  return wuffs_base__pixel_buffer__pixel_format(this);\n}\n\ninline wuffs_base__table_u8  //\nwuffs_base__pixel_buffer::plane(uint32_t p) {\n  return wuffs_base__pixel_buffer__plane(this, p);\n}\n\n#endif  // __cplusplus\n\n" +
 	"" +
 	"// --------\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    uint8_t TODO;\n  } private_impl;\n\n#ifdef __cplusplus\n#endif  // __cplusplus\n\n} wuffs_base__decode_frame_options;\n\n#ifdef __cplusplus\n\n#endif  // __cplusplus\n\n" +
 	"" +
-	"// --------\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    // TODO: should the func type take restrict pointers?\n    uint64_t (*func)(wuffs_base__slice_u8 dst,\n                     wuffs_base__slice_u8 dst_palette,\n                     wuffs_base__slice_u8 src);\n  } private_impl;\n\n#ifdef __cplusplus\n  inline void prepare(wuffs_base__pixel_format dst_format,\n                      wuffs_base__slice_u8 dst_palette,\n                      wuffs_base__pixel_format src_format,\n                      wuffs_base__slice_u8 src_palette);\n  inline uint64_t swizzle_packed(wuffs_base__slice_u8 dst,\n                                 wuffs_base__slice_u8 dst_palette,\n                                 wuffs_base__slice_u8 src);\n#endif  // __cplusplus\n\n} wuffs_base__pixel_swizzler;\n\n// TODO: should prepare (both the C and C++ methods) return a status?\n\nvoid  //\nwuffs_base__pixel_swizzler__prepare(wuffs_base__pixel_swi" +
-	"zzler* p,\n                                    wuffs_base__pixel_format dst_format,\n                                    wuffs_base__slice_u8 dst_palette,\n                                    wuffs_base__pixel_format src_format,\n                                    wuffs_base__slice_u8 src_palette);\n\nuint64_t  //\nwuffs_base__pixel_swizzler__swizzle_packed(wuffs_base__pixel_swizzler* p,\n                                           wuffs_base__slice_u8 dst,\n                                           wuffs_base__slice_u8 dst_palette,\n                                           wuffs_base__slice_u8 src);\n\n#ifdef __cplusplus\n\ninline void  //\nwuffs_base__pixel_swizzler::prepare(wuffs_base__pixel_format dst_format,\n                                    wuffs_base__slice_u8 dst_palette,\n                                    wuffs_base__pixel_format src_format,\n                                    wuffs_base__slice_u8 src_palette) {\n  wuffs_base__pixel_swizzler__prepare(this, dst_format, dst_palette, src_format,\n                 " +
-	"                     src_palette);\n}\n\nuint64_t  //\nwuffs_base__pixel_swizzler::swizzle_packed(wuffs_base__slice_u8 dst,\n                                           wuffs_base__slice_u8 dst_palette,\n                                           wuffs_base__slice_u8 src) {\n  return wuffs_base__pixel_swizzler__swizzle_packed(this, dst, dst_palette,\n                                                    src);\n}\n\n#endif  // __cplusplus\n" +
+	"// --------\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    // TODO: should the func type take restrict pointers?\n    uint64_t (*func)(wuffs_base__slice_u8 dst,\n                     wuffs_base__slice_u8 dst_palette,\n                     wuffs_base__slice_u8 src);\n  } private_impl;\n\n#ifdef __cplusplus\n  inline wuffs_base__status prepare(wuffs_base__pixel_format dst_format,\n                                    wuffs_base__slice_u8 dst_palette,\n                                    wuffs_base__pixel_format src_format,\n                                    wuffs_base__slice_u8 src_palette);\n  inline uint64_t swizzle_interleaved(wuffs_base__slice_u8 dst,\n                                      wuffs_base__slice_u8 dst_palette,\n                                      wuffs_base__slice_u8 src) const;\n#endif  // __cplusplus\n\n} wuffs_base__pixel_swizzler;\n\nwuffs_base__status  //\nwuffs_base__pixel_swizzler__prepare(w" +
+	"uffs_base__pixel_swizzler* p,\n                                    wuffs_base__pixel_format dst_format,\n                                    wuffs_base__slice_u8 dst_palette,\n                                    wuffs_base__pixel_format src_format,\n                                    wuffs_base__slice_u8 src_palette);\n\nuint64_t  //\nwuffs_base__pixel_swizzler__swizzle_interleaved(\n    const wuffs_base__pixel_swizzler* p,\n    wuffs_base__slice_u8 dst,\n    wuffs_base__slice_u8 dst_palette,\n    wuffs_base__slice_u8 src);\n\n#ifdef __cplusplus\n\ninline wuffs_base__status  //\nwuffs_base__pixel_swizzler::prepare(wuffs_base__pixel_format dst_format,\n                                    wuffs_base__slice_u8 dst_palette,\n                                    wuffs_base__pixel_format src_format,\n                                    wuffs_base__slice_u8 src_palette) {\n  return wuffs_base__pixel_swizzler__prepare(this, dst_format, dst_palette,\n                                             src_format, src_palette);\n}\n\nuint64_t  //\nwu" +
+	"ffs_base__pixel_swizzler::swizzle_interleaved(\n    wuffs_base__slice_u8 dst,\n    wuffs_base__slice_u8 dst_palette,\n    wuffs_base__slice_u8 src) const {\n  return wuffs_base__pixel_swizzler__swizzle_interleaved(this, dst, dst_palette,\n                                                         src);\n}\n\n#endif  // __cplusplus\n" +
 	""
 
 const baseIOPrivateH = "" +
-	"// ---------------- I/O\n\nstatic inline bool  //\nwuffs_base__io_buffer__is_valid(wuffs_base__io_buffer buf) {\n  return (buf.data.ptr || (buf.data.len == 0)) &&\n         (buf.data.len >= buf.meta.wi) && (buf.meta.wi >= buf.meta.ri);\n}\n\n// TODO: wuffs_base__io_reader__is_eof is no longer used by Wuffs per se, but\n// it might be handy to programs that use Wuffs. Either delete it, or promote\n// it to the public API.\n//\n// If making this function public (i.e. moving it to base-header.h), it also\n// needs to allow NULL (i.e. implicit, callee-calculated) mark/limit.\n\nstatic inline bool  //\nwuffs_base__io_reader__is_eof(wuffs_base__io_reader o) {\n  wuffs_base__io_buffer* buf = o.private_impl.buf;\n  return buf && buf->meta.closed &&\n         (buf->data.ptr + buf->meta.wi == o.private_impl.limit);\n}\n\nstatic inline bool  //\nwuffs_base__io_reader__is_valid(wuffs_base__io_reader o) {\n  wuffs_base__io_buffer* buf = o.private_impl.buf;\n  // Note: if making this function public (i.e. moving it to base-header.h), it\n  // also " +
-	"needs to allow NULL (i.e. implicit, callee-calculated) mark/limit.\n  return buf ? ((buf->data.ptr <= o.private_impl.mark) &&\n                (o.private_impl.mark <= o.private_impl.limit) &&\n                (o.private_impl.limit <= buf->data.ptr + buf->data.len))\n             : ((o.private_impl.mark == NULL) &&\n                (o.private_impl.limit == NULL));\n}\n\nstatic inline bool  //\nwuffs_base__io_writer__is_valid(wuffs_base__io_writer o) {\n  wuffs_base__io_buffer* buf = o.private_impl.buf;\n  // Note: if making this function public (i.e. moving it to base-header.h), it\n  // also needs to allow NULL (i.e. implicit, callee-calculated) mark/limit.\n  return buf ? ((buf->data.ptr <= o.private_impl.mark) &&\n                (o.private_impl.mark <= o.private_impl.limit) &&\n                (o.private_impl.limit <= buf->data.ptr + buf->data.len))\n             : ((o.private_impl.mark == NULL) &&\n                (o.private_impl.limit == NULL));\n}\n\nstatic inline uint32_t  //\nwuffs_base__io_writer__copy_n_from_history(uin" +
-	"t8_t** ptr_iop_w,\n                                           uint8_t* io0_w,\n                                           uint8_t* io1_w,\n                                           uint32_t length,\n                                           uint32_t distance) {\n  if (!distance) {\n    return 0;\n  }\n  uint8_t* p = *ptr_iop_w;\n  if ((size_t)(p - io0_w) < (size_t)(distance)) {\n    return 0;\n  }\n  uint8_t* q = p - distance;\n  size_t n = (size_t)(io1_w - p);\n  if ((size_t)(length) > n) {\n    length = (uint32_t)(n);\n  } else {\n    n = (size_t)(length);\n  }\n  // TODO: unrolling by 3 seems best for the std/deflate benchmarks, but that\n  // is mostly because 3 is the minimum length for the deflate format. This\n  // function implementation shouldn't overfit to that one format. Perhaps the\n  // copy_n_from_history Wuffs method should also take an unroll hint argument,\n  // and the cgen can look if that argument is the constant expression '3'.\n  //\n  // See also wuffs_base__io_writer__copy_n_from_history_fast below.\n  //\n  " +
-	"// Alternatively, or additionally, have a sloppy_copy_n_from_history method\n  // that copies 8 bytes at a time, possibly writing more than length bytes?\n  for (; n >= 3; n -= 3) {\n    *p++ = *q++;\n    *p++ = *q++;\n    *p++ = *q++;\n  }\n  for (; n; n--) {\n    *p++ = *q++;\n  }\n  *ptr_iop_w = p;\n  return length;\n}\n\n// wuffs_base__io_writer__copy_n_from_history_fast is like the\n// wuffs_base__io_writer__copy_n_from_history function above, but has stronger\n// pre-conditions. The caller needs to prove that:\n//  - distance >  0\n//  - distance <= (*ptr_iop_w - io0_w)\n//  - length   <= (io1_w      - *ptr_iop_w)\nstatic inline uint32_t  //\nwuffs_base__io_writer__copy_n_from_history_fast(uint8_t** ptr_iop_w,\n                                                uint8_t* io0_w,\n                                                uint8_t* io1_w,\n                                                uint32_t length,\n                                                uint32_t distance) {\n  uint8_t* p = *ptr_iop_w;\n  uint8_t* q = p - distance;\n " +
-	" uint32_t n = length;\n  for (; n >= 3; n -= 3) {\n    *p++ = *q++;\n    *p++ = *q++;\n    *p++ = *q++;\n  }\n  for (; n; n--) {\n    *p++ = *q++;\n  }\n  *ptr_iop_w = p;\n  return length;\n}\n\nstatic inline uint32_t  //\nwuffs_base__io_writer__copy_n_from_reader(uint8_t** ptr_iop_w,\n                                          uint8_t* io1_w,\n                                          uint32_t length,\n                                          uint8_t** ptr_iop_r,\n                                          uint8_t* io1_r) {\n  uint8_t* iop_w = *ptr_iop_w;\n  size_t n = length;\n  if (n > ((size_t)(io1_w - iop_w))) {\n    n = (size_t)(io1_w - iop_w);\n  }\n  uint8_t* iop_r = *ptr_iop_r;\n  if (n > ((size_t)(io1_r - iop_r))) {\n    n = (size_t)(io1_r - iop_r);\n  }\n  if (n > 0) {\n    memmove(iop_w, iop_r, n);\n    *ptr_iop_w += n;\n    *ptr_iop_r += n;\n  }\n  return (uint32_t)(n);\n}\n\nstatic inline uint64_t  //\nwuffs_base__io_writer__copy_from_slice(uint8_t** ptr_iop_w,\n                                       uint8_t* io1_w,\n                 " +
-	"                      wuffs_base__slice_u8 src) {\n  uint8_t* iop_w = *ptr_iop_w;\n  size_t n = src.len;\n  if (n > ((size_t)(io1_w - iop_w))) {\n    n = (size_t)(io1_w - iop_w);\n  }\n  if (n > 0) {\n    memmove(iop_w, src.ptr, n);\n    *ptr_iop_w += n;\n  }\n  return (uint64_t)(n);\n}\n\nstatic inline uint32_t  //\nwuffs_base__io_writer__copy_n_from_slice(uint8_t** ptr_iop_w,\n                                         uint8_t* io1_w,\n                                         uint32_t length,\n                                         wuffs_base__slice_u8 src) {\n  uint8_t* iop_w = *ptr_iop_w;\n  size_t n = src.len;\n  if (n > length) {\n    n = length;\n  }\n  if (n > ((size_t)(io1_w - iop_w))) {\n    n = (size_t)(io1_w - iop_w);\n  }\n  if (n > 0) {\n    memmove(iop_w, src.ptr, n);\n    *ptr_iop_w += n;\n  }\n  return (uint32_t)(n);\n}\n\nstatic inline wuffs_base__empty_struct  //\nwuffs_base__io_reader__set(wuffs_base__io_reader* o,\n                           wuffs_base__io_buffer* b,\n                           uint8_t** ptr_iop_r,\n        " +
-	"                   uint8_t** ptr_io1_r,\n                           wuffs_base__slice_u8 data) {\n  b->data = data;\n  b->meta.wi = data.len;\n  b->meta.ri = 0;\n  b->meta.pos = 0;\n  b->meta.closed = false;\n\n  o->private_impl.buf = b;\n  o->private_impl.mark = data.ptr;\n  o->private_impl.limit = data.ptr + data.len;\n  *ptr_iop_r = data.ptr;\n  *ptr_io1_r = data.ptr + data.len;\n\n  wuffs_base__empty_struct ret;\n  ret.private_impl = 0;\n  return ret;\n}\n\nstatic inline wuffs_base__empty_struct  //\nwuffs_base__io_reader__set_limit(wuffs_base__io_reader* o,\n                                 uint8_t* iop_r,\n                                 uint64_t limit) {\n  if (o && (((size_t)(o->private_impl.limit - iop_r)) > limit)) {\n    o->private_impl.limit = iop_r + limit;\n  }\n\n  wuffs_base__empty_struct ret;\n  ret.private_impl = 0;\n  return ret;\n}\n\nstatic inline wuffs_base__empty_struct  //\nwuffs_base__io_reader__set_mark(wuffs_base__io_reader* o, uint8_t* mark) {\n  o->private_impl.mark = mark;\n\n  wuffs_base__empty_struct ret;\n  ret." +
-	"private_impl = 0;\n  return ret;\n}\n\nstatic inline wuffs_base__slice_u8  //\nwuffs_base__io_reader__take(uint8_t** ptr_iop_r, uint8_t* io1_r, uint64_t n) {\n  if (n <= ((size_t)(io1_r - *ptr_iop_r))) {\n    uint8_t* p = *ptr_iop_r;\n    *ptr_iop_r += n;\n    return wuffs_base__make_slice_u8(p, n);\n  }\n  return wuffs_base__make_slice_u8(NULL, 0);\n}\n\nstatic inline wuffs_base__empty_struct  //\nwuffs_base__io_writer__set(wuffs_base__io_writer* o,\n                           wuffs_base__io_buffer* b,\n                           uint8_t** ptr_iop_w,\n                           uint8_t** ptr_io1_w,\n                           wuffs_base__slice_u8 data) {\n  b->data = data;\n  b->meta.wi = 0;\n  b->meta.ri = 0;\n  b->meta.pos = 0;\n  b->meta.closed = false;\n\n  o->private_impl.buf = b;\n  o->private_impl.mark = data.ptr;\n  o->private_impl.limit = data.ptr + data.len;\n  *ptr_iop_w = data.ptr;\n  *ptr_io1_w = data.ptr + data.len;\n\n  wuffs_base__empty_struct ret;\n  ret.private_impl = 0;\n  return ret;\n}\n\nstatic inline wuffs_base__empty_str" +
-	"uct  //\nwuffs_base__io_writer__set_mark(wuffs_base__io_writer* o, uint8_t* mark) {\n  o->private_impl.mark = mark;\n\n  wuffs_base__empty_struct ret;\n  ret.private_impl = 0;\n  return ret;\n}\n\n" +
+	"// ---------------- I/O\n\n// \"Null\" as in \"/dev/null\", not as in \"nullptr\".\n//\n// TODO: ensure that this is zero-initialized.\nstatic wuffs_base__io_buffer wuffs_base__global__null_io_buffer;\n\nstatic inline wuffs_base__io_buffer*  //\nwuffs_base__null_io_reader() {\n  return &wuffs_base__global__null_io_buffer;\n}\n\nstatic inline wuffs_base__io_buffer*  //\nwuffs_base__null_io_writer() {\n  return &wuffs_base__global__null_io_buffer;\n}\n\nstatic inline uint64_t  //\nwuffs_base__io__count_since(uint64_t mark, uint64_t index) {\n  if (index >= mark) {\n    return index - mark;\n  }\n  return 0;\n}\n\nstatic inline wuffs_base__slice_u8  //\nwuffs_base__io__since(uint64_t mark, uint64_t index, uint8_t* ptr) {\n  if (index >= mark) {\n    return wuffs_base__make_slice_u8(ptr + mark, index - mark);\n  }\n  return wuffs_base__make_slice_u8(NULL, 0);\n}\n\nstatic inline uint32_t  //\nwuffs_base__io_writer__copy_n_from_history(uint8_t** ptr_iop_w,\n                                           uint8_t* io1_w,\n                                       " +
+	"    uint8_t* io2_w,\n                                           uint32_t length,\n                                           uint32_t distance) {\n  if (!distance) {\n    return 0;\n  }\n  uint8_t* p = *ptr_iop_w;\n  if ((size_t)(p - io1_w) < (size_t)(distance)) {\n    return 0;\n  }\n  uint8_t* q = p - distance;\n  size_t n = (size_t)(io2_w - p);\n  if ((size_t)(length) > n) {\n    length = (uint32_t)(n);\n  } else {\n    n = (size_t)(length);\n  }\n  // TODO: unrolling by 3 seems best for the std/deflate benchmarks, but that\n  // is mostly because 3 is the minimum length for the deflate format. This\n  // function implementation shouldn't overfit to that one format. Perhaps the\n  // copy_n_from_history Wuffs method should also take an unroll hint argument,\n  // and the cgen can look if that argument is the constant expression '3'.\n  //\n  // See also wuffs_base__io_writer__copy_n_from_history_fast below.\n  //\n  // Alternatively, or additionally, have a sloppy_copy_n_from_history method\n  // that copies 8 bytes at a time, poss" +
+	"ibly writing more than length bytes?\n  for (; n >= 3; n -= 3) {\n    *p++ = *q++;\n    *p++ = *q++;\n    *p++ = *q++;\n  }\n  for (; n; n--) {\n    *p++ = *q++;\n  }\n  *ptr_iop_w = p;\n  return length;\n}\n\n// wuffs_base__io_writer__copy_n_from_history_fast is like the\n// wuffs_base__io_writer__copy_n_from_history function above, but has stronger\n// pre-conditions. The caller needs to prove that:\n//  - distance >  0\n//  - distance <= (*ptr_iop_w - io1_w)\n//  - length   <= (io2_w      - *ptr_iop_w)\nstatic inline uint32_t  //\nwuffs_base__io_writer__copy_n_from_history_fast(uint8_t** ptr_iop_w,\n                                                uint8_t* io1_w,\n                                                uint8_t* io2_w,\n                                                uint32_t length,\n                                                uint32_t distance) {\n  uint8_t* p = *ptr_iop_w;\n  uint8_t* q = p - distance;\n  uint32_t n = length;\n  for (; n >= 3; n -= 3) {\n    *p++ = *q++;\n    *p++ = *q++;\n    *p++ = *q++;\n  }\n  for (; n; " +
+	"n--) {\n    *p++ = *q++;\n  }\n  *ptr_iop_w = p;\n  return length;\n}\n\nstatic inline uint32_t  //\nwuffs_base__io_writer__copy_n_from_reader(uint8_t** ptr_iop_w,\n                                          uint8_t* io2_w,\n                                          uint32_t length,\n                                          uint8_t** ptr_iop_r,\n                                          uint8_t* io2_r) {\n  uint8_t* iop_w = *ptr_iop_w;\n  size_t n = length;\n  if (n > ((size_t)(io2_w - iop_w))) {\n    n = (size_t)(io2_w - iop_w);\n  }\n  uint8_t* iop_r = *ptr_iop_r;\n  if (n > ((size_t)(io2_r - iop_r))) {\n    n = (size_t)(io2_r - iop_r);\n  }\n  if (n > 0) {\n    memmove(iop_w, iop_r, n);\n    *ptr_iop_w += n;\n    *ptr_iop_r += n;\n  }\n  return (uint32_t)(n);\n}\n\nstatic inline uint64_t  //\nwuffs_base__io_writer__copy_from_slice(uint8_t** ptr_iop_w,\n                                       uint8_t* io2_w,\n                                       wuffs_base__slice_u8 src) {\n  uint8_t* iop_w = *ptr_iop_w;\n  size_t n = src.len;\n  if (n > ((s" +
+	"ize_t)(io2_w - iop_w))) {\n    n = (size_t)(io2_w - iop_w);\n  }\n  if (n > 0) {\n    memmove(iop_w, src.ptr, n);\n    *ptr_iop_w += n;\n  }\n  return (uint64_t)(n);\n}\n\nstatic inline uint32_t  //\nwuffs_base__io_writer__copy_n_from_slice(uint8_t** ptr_iop_w,\n                                         uint8_t* io2_w,\n                                         uint32_t length,\n                                         wuffs_base__slice_u8 src) {\n  uint8_t* iop_w = *ptr_iop_w;\n  size_t n = src.len;\n  if (n > length) {\n    n = length;\n  }\n  if (n > ((size_t)(io2_w - iop_w))) {\n    n = (size_t)(io2_w - iop_w);\n  }\n  if (n > 0) {\n    memmove(iop_w, src.ptr, n);\n    *ptr_iop_w += n;\n  }\n  return (uint32_t)(n);\n}\n\nstatic inline wuffs_base__io_buffer*  //\nwuffs_base__io_reader__set(wuffs_base__io_buffer* b,\n                           uint8_t** ptr_iop_r,\n                           uint8_t** ptr_io0_r,\n                           uint8_t** ptr_io1_r,\n                           uint8_t** ptr_io2_r,\n                           wuffs_ba" +
+	"se__slice_u8 data) {\n  b->data = data;\n  b->meta.wi = data.len;\n  b->meta.ri = 0;\n  b->meta.pos = 0;\n  b->meta.closed = false;\n\n  *ptr_iop_r = data.ptr;\n  *ptr_io0_r = data.ptr;\n  *ptr_io1_r = data.ptr;\n  *ptr_io2_r = data.ptr + data.len;\n\n  return b;\n}\n\nstatic inline wuffs_base__slice_u8  //\nwuffs_base__io_reader__take(uint8_t** ptr_iop_r, uint8_t* io2_r, uint64_t n) {\n  if (n <= ((size_t)(io2_r - *ptr_iop_r))) {\n    uint8_t* p = *ptr_iop_r;\n    *ptr_iop_r += n;\n    return wuffs_base__make_slice_u8(p, n);\n  }\n  return wuffs_base__make_slice_u8(NULL, 0);\n}\n\nstatic inline wuffs_base__io_buffer*  //\nwuffs_base__io_writer__set(wuffs_base__io_buffer* b,\n                           uint8_t** ptr_iop_w,\n                           uint8_t** ptr_io0_w,\n                           uint8_t** ptr_io1_w,\n                           uint8_t** ptr_io2_w,\n                           wuffs_base__slice_u8 data) {\n  b->data = data;\n  b->meta.wi = 0;\n  b->meta.ri = 0;\n  b->meta.pos = 0;\n  b->meta.closed = false;\n\n  *ptr_iop_w = dat" +
+	"a.ptr;\n  *ptr_io0_w = data.ptr;\n  *ptr_io1_w = data.ptr;\n  *ptr_io2_w = data.ptr + data.len;\n\n  return b;\n}\n\n" +
 	"" +
 	"// ---------------- I/O (Utility)\n\n#define wuffs_base__utility__null_io_reader wuffs_base__null_io_reader\n#define wuffs_base__utility__null_io_writer wuffs_base__null_io_writer\n" +
 	""
 
 const baseIOPublicH = "" +
-	"// ---------------- I/O\n\nstruct wuffs_base__io_buffer__struct;\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    struct wuffs_base__io_buffer__struct* 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* mark;\n    uint8_t* limit;\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/ABI\n  // compatibility or safety guarantee if you do so.\n  struct {\n    struct wuffs_base__io_buffer__struct* 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* mark;\n    uint8_t* limit;\n  } private_impl;\n} wuffs_base__io_writer;\n\n// wuffs_base__io_buffer_meta is the met" +
-	"adata for a wuffs_base__io_buffer's\n// data.\ntypedef struct {\n  size_t wi;     // Write index. Invariant: wi <= len.\n  size_t ri;     // Read  index. Invariant: ri <= wi.\n  uint64_t pos;  // Position of the buffer start relative to the stream start.\n  bool closed;   // No further writes are expected.\n} wuffs_base__io_buffer_meta;\n\n// wuffs_base__io_buffer is a 1-dimensional buffer (a pointer and length) plus\n// additional metadata.\n//\n// A value with all fields zero is a valid, empty buffer.\ntypedef struct wuffs_base__io_buffer__struct {\n  wuffs_base__slice_u8 data;\n  wuffs_base__io_buffer_meta meta;\n\n#ifdef __cplusplus\n  inline void compact();\n  inline wuffs_base__io_reader reader();\n  inline wuffs_base__io_writer writer();\n  inline uint64_t reader_io_position();\n  inline uint64_t writer_io_position();\n#endif  // __cplusplus\n\n} wuffs_base__io_buffer;\n\nstatic inline wuffs_base__io_buffer  //\nwuffs_base__make_io_buffer(wuffs_base__slice_u8 data,\n                           wuffs_base__io_buffer_meta meta) {\n  w" +
-	"uffs_base__io_buffer ret;\n  ret.data = data;\n  ret.meta = meta;\n  return ret;\n}\n\nstatic inline wuffs_base__io_buffer_meta  //\nwuffs_base__make_io_buffer_meta(size_t wi,\n                                size_t ri,\n                                uint64_t pos,\n                                bool closed) {\n  wuffs_base__io_buffer_meta ret;\n  ret.wi = wi;\n  ret.ri = ri;\n  ret.pos = pos;\n  ret.closed = closed;\n  return ret;\n}\n\nstatic inline wuffs_base__io_buffer  //\nwuffs_base__null_io_buffer() {\n  wuffs_base__io_buffer ret;\n  ret.data.ptr = NULL;\n  ret.data.len = 0;\n  ret.meta.wi = 0;\n  ret.meta.ri = 0;\n  ret.meta.pos = 0;\n  ret.meta.closed = false;\n  return ret;\n}\n\nstatic inline wuffs_base__io_buffer_meta  //\nwuffs_base__null_io_buffer_meta() {\n  wuffs_base__io_buffer_meta ret;\n  ret.wi = 0;\n  ret.ri = 0;\n  ret.pos = 0;\n  ret.closed = false;\n  return ret;\n}\n\nstatic inline wuffs_base__io_reader  //\nwuffs_base__null_io_reader() {\n  wuffs_base__io_reader ret;\n  ret.private_impl.buf = NULL;\n  ret.private_impl.mark =" +
-	" NULL;\n  ret.private_impl.limit = NULL;\n  return ret;\n}\n\nstatic inline wuffs_base__io_writer  //\nwuffs_base__null_io_writer() {\n  wuffs_base__io_writer ret;\n  ret.private_impl.buf = NULL;\n  ret.private_impl.mark = NULL;\n  ret.private_impl.limit = NULL;\n  return ret;\n}\n\n// wuffs_base__io_buffer__compact moves any written but unread bytes to the\n// start of the buffer.\nstatic inline void  //\nwuffs_base__io_buffer__compact(wuffs_base__io_buffer* buf) {\n  if (!buf || (buf->meta.ri == 0)) {\n    return;\n  }\n  buf->meta.pos = wuffs_base__u64__sat_add(buf->meta.pos, buf->meta.ri);\n  size_t n = buf->meta.wi - buf->meta.ri;\n  if (n != 0) {\n    memmove(buf->data.ptr, buf->data.ptr + buf->meta.ri, n);\n  }\n  buf->meta.wi = n;\n  buf->meta.ri = 0;\n}\n\nstatic inline wuffs_base__io_reader  //\nwuffs_base__io_buffer__reader(wuffs_base__io_buffer* buf) {\n  wuffs_base__io_reader ret;\n  ret.private_impl.buf = buf;\n  ret.private_impl.mark = NULL;\n  ret.private_impl.limit = NULL;\n  return ret;\n}\n\nstatic inline wuffs_base__io_writer  " +
-	"//\nwuffs_base__io_buffer__writer(wuffs_base__io_buffer* buf) {\n  wuffs_base__io_writer ret;\n  ret.private_impl.buf = buf;\n  ret.private_impl.mark = NULL;\n  ret.private_impl.limit = NULL;\n  return ret;\n}\n\nstatic inline uint64_t  //\nwuffs_base__io_buffer__reader_io_position(wuffs_base__io_buffer* buf) {\n  return buf ? wuffs_base__u64__sat_add(buf->meta.pos, buf->meta.ri) : 0;\n}\n\nstatic inline uint64_t  //\nwuffs_base__io_buffer__writer_io_position(wuffs_base__io_buffer* buf) {\n  return buf ? wuffs_base__u64__sat_add(buf->meta.pos, buf->meta.wi) : 0;\n}\n\n#ifdef __cplusplus\n\ninline void  //\nwuffs_base__io_buffer__struct::compact() {\n  wuffs_base__io_buffer__compact(this);\n}\n\ninline wuffs_base__io_reader  //\nwuffs_base__io_buffer__struct::reader() {\n  return wuffs_base__io_buffer__reader(this);\n}\n\ninline wuffs_base__io_writer  //\nwuffs_base__io_buffer__struct::writer() {\n  return wuffs_base__io_buffer__writer(this);\n}\n\ninline uint64_t  //\nwuffs_base__io_buffer__struct::reader_io_position() {\n  return wuffs_base__io_" +
-	"buffer__reader_io_position(this);\n}\n\ninline uint64_t  //\nwuffs_base__io_buffer__struct::writer_io_position() {\n  return wuffs_base__io_buffer__writer_io_position(this);\n}\n\n#endif  // __cplusplus\n" +
+	"// ---------------- I/O\n//\n// See (/doc/note/io-input-output.md).\n\n// wuffs_base__io_buffer_meta is the metadata for a wuffs_base__io_buffer's\n// data.\ntypedef struct {\n  size_t wi;     // Write index. Invariant: wi <= len.\n  size_t ri;     // Read  index. Invariant: ri <= wi.\n  uint64_t pos;  // Position of the buffer start relative to the stream start.\n  bool closed;   // No further writes are expected.\n} wuffs_base__io_buffer_meta;\n\n// wuffs_base__io_buffer is a 1-dimensional buffer (a pointer and length) plus\n// additional metadata.\n//\n// A value with all fields zero is a valid, empty buffer.\ntypedef struct wuffs_base__io_buffer__struct {\n  wuffs_base__slice_u8 data;\n  wuffs_base__io_buffer_meta meta;\n\n#ifdef __cplusplus\n  inline void compact();\n  inline wuffs_base__io_buffer__struct* reader();  // Deprecated.\n  inline wuffs_base__io_buffer__struct* writer();  // Deprecated.\n  inline uint64_t reader_available() const;\n  inline uint64_t reader_io_position() const;\n  inline uint64_t writer_available() const" +
+	";\n  inline uint64_t writer_io_position() const;\n#endif  // __cplusplus\n\n} wuffs_base__io_buffer;\n\nstatic inline wuffs_base__io_buffer  //\nwuffs_base__make_io_buffer(wuffs_base__slice_u8 data,\n                           wuffs_base__io_buffer_meta meta) {\n  wuffs_base__io_buffer ret;\n  ret.data = data;\n  ret.meta = meta;\n  return ret;\n}\n\nstatic inline wuffs_base__io_buffer_meta  //\nwuffs_base__make_io_buffer_meta(size_t wi,\n                                size_t ri,\n                                uint64_t pos,\n                                bool closed) {\n  wuffs_base__io_buffer_meta ret;\n  ret.wi = wi;\n  ret.ri = ri;\n  ret.pos = pos;\n  ret.closed = closed;\n  return ret;\n}\n\nstatic inline wuffs_base__io_buffer  //\nwuffs_base__null_io_buffer() {\n  wuffs_base__io_buffer ret;\n  ret.data.ptr = NULL;\n  ret.data.len = 0;\n  ret.meta.wi = 0;\n  ret.meta.ri = 0;\n  ret.meta.pos = 0;\n  ret.meta.closed = false;\n  return ret;\n}\n\nstatic inline wuffs_base__io_buffer_meta  //\nwuffs_base__null_io_buffer_meta() {\n  wuffs_base__i" +
+	"o_buffer_meta ret;\n  ret.wi = 0;\n  ret.ri = 0;\n  ret.pos = 0;\n  ret.closed = false;\n  return ret;\n}\n\n// wuffs_base__io_buffer__compact moves any written but unread bytes to the\n// start of the buffer.\nstatic inline void  //\nwuffs_base__io_buffer__compact(wuffs_base__io_buffer* buf) {\n  if (!buf || (buf->meta.ri == 0)) {\n    return;\n  }\n  buf->meta.pos = wuffs_base__u64__sat_add(buf->meta.pos, buf->meta.ri);\n  size_t n = buf->meta.wi - buf->meta.ri;\n  if (n != 0) {\n    memmove(buf->data.ptr, buf->data.ptr + buf->meta.ri, n);\n  }\n  buf->meta.wi = n;\n  buf->meta.ri = 0;\n}\n\nstatic inline uint64_t  //\nwuffs_base__io_buffer__reader_available(const wuffs_base__io_buffer* buf) {\n  return buf ? buf->meta.wi - buf->meta.ri : 0;\n}\n\nstatic inline uint64_t  //\nwuffs_base__io_buffer__reader_io_position(const wuffs_base__io_buffer* buf) {\n  return buf ? wuffs_base__u64__sat_add(buf->meta.pos, buf->meta.ri) : 0;\n}\n\nstatic inline uint64_t  //\nwuffs_base__io_buffer__writer_available(const wuffs_base__io_buffer* buf) {\n  return" +
+	" buf ? buf->data.len - buf->meta.wi : 0;\n}\n\nstatic inline uint64_t  //\nwuffs_base__io_buffer__writer_io_position(const wuffs_base__io_buffer* buf) {\n  return buf ? wuffs_base__u64__sat_add(buf->meta.pos, buf->meta.wi) : 0;\n}\n\n#ifdef __cplusplus\n\ninline void  //\nwuffs_base__io_buffer__struct::compact() {\n  wuffs_base__io_buffer__compact(this);\n}\n\ninline wuffs_base__io_buffer*  //\nwuffs_base__io_buffer__struct::reader() {\n  return this;\n}\n\ninline wuffs_base__io_buffer*  //\nwuffs_base__io_buffer__struct::writer() {\n  return this;\n}\n\ninline uint64_t  //\nwuffs_base__io_buffer__struct::reader_available() const {\n  return wuffs_base__io_buffer__reader_available(this);\n}\n\ninline uint64_t  //\nwuffs_base__io_buffer__struct::reader_io_position() const {\n  return wuffs_base__io_buffer__reader_io_position(this);\n}\n\ninline uint64_t  //\nwuffs_base__io_buffer__struct::writer_available() const {\n  return wuffs_base__io_buffer__writer_available(this);\n}\n\ninline uint64_t  //\nwuffs_base__io_buffer__struct::writer_io_position() c" +
+	"onst {\n  return wuffs_base__io_buffer__writer_io_position(this);\n}\n\n#endif  // __cplusplus\n" +
 	""
 
 const baseRangePrivateH = "" +
@@ -186,39 +191,39 @@
 
 const baseRangePublicH = "" +
 	"// ---------------- Ranges and Rects\n\n// Ranges are either inclusive (\"range_ii\") or exclusive (\"range_ie\") on the\n// high end. Both the \"ii\" and \"ie\" flavors are useful in practice.\n//\n// The \"ei\" and \"ee\" flavors also exist in theory, but aren't widely used. In\n// Wuffs, the low end is always inclusive.\n//\n// The \"ii\" (closed interval) flavor is useful when refining e.g. \"the set of\n// all uint32_t values\" to a contiguous subset: \"uint32_t values in the closed\n// interval [M, N]\", for uint32_t values M and N. An unrefined type (in other\n// words, the set of all uint32_t values) is not representable in the \"ie\"\n// flavor because if N equals ((1<<32) - 1) then (N + 1) will overflow.\n//\n// On the other hand, the \"ie\" (half-open interval) flavor is recommended by\n// Dijkstra's \"Why numbering should start at zero\" at\n// http://www.cs.utexas.edu/users/EWD/ewd08xx/EWD831.PDF and a further\n// discussion of motivating rationale is at\n// https://www.quora.com/Why-are-Python-ranges-half-open-exclusive-instead-of-close" +
-	"d-inclusive\n//\n// For example, with \"ie\", the number of elements in \"uint32_t values in the\n// half-open interval [M, N)\" is equal to max(0, N-M). Furthermore, that number\n// of elements (in one dimension, a length, in two dimensions, a width or\n// height) is itself representable as a uint32_t without overflow, again for\n// uint32_t values M and N. In the contrasting \"ii\" flavor, the length of the\n// closed interval [0, (1<<32) - 1] is 1<<32, which cannot be represented as a\n// uint32_t. In Wuffs, because of this potential overflow, the \"ie\" flavor has\n// length / width / height methods, but the \"ii\" flavor does not.\n//\n// It is valid for min > max (for range_ii) or for min >= max (for range_ie),\n// in which case the range is empty. There are multiple representations of an\n// empty range.\n\ntypedef struct wuffs_base__range_ii_u32__struct {\n  uint32_t min_incl;\n  uint32_t max_incl;\n\n#ifdef __cplusplus\n  inline bool is_empty();\n  inline bool equals(wuffs_base__range_ii_u32__struct s);\n  inline wuffs_base__range_" +
-	"ii_u32__struct intersect(\n      wuffs_base__range_ii_u32__struct s);\n  inline wuffs_base__range_ii_u32__struct unite(\n      wuffs_base__range_ii_u32__struct s);\n  inline bool contains(uint32_t x);\n  inline bool contains_range(wuffs_base__range_ii_u32__struct s);\n#endif  // __cplusplus\n\n} wuffs_base__range_ii_u32;\n\nstatic inline wuffs_base__range_ii_u32  //\nwuffs_base__make_range_ii_u32(uint32_t min_incl, uint32_t max_incl) {\n  wuffs_base__range_ii_u32 ret;\n  ret.min_incl = min_incl;\n  ret.max_incl = max_incl;\n  return ret;\n}\n\nstatic inline bool  //\nwuffs_base__range_ii_u32__is_empty(wuffs_base__range_ii_u32* r) {\n  return r->min_incl > r->max_incl;\n}\n\nstatic inline bool  //\nwuffs_base__range_ii_u32__equals(wuffs_base__range_ii_u32* r,\n                                 wuffs_base__range_ii_u32 s) {\n  return (r->min_incl == s.min_incl && r->max_incl == s.max_incl) ||\n         (wuffs_base__range_ii_u32__is_empty(r) &&\n          wuffs_base__range_ii_u32__is_empty(&s));\n}\n\nstatic inline wuffs_base__range_ii_u32  //" +
-	"\nwuffs_base__range_ii_u32__intersect(wuffs_base__range_ii_u32* r,\n                                    wuffs_base__range_ii_u32 s) {\n  wuffs_base__range_ii_u32 t;\n  t.min_incl = wuffs_base__u32__max(r->min_incl, s.min_incl);\n  t.max_incl = wuffs_base__u32__min(r->max_incl, s.max_incl);\n  return t;\n}\n\nstatic inline wuffs_base__range_ii_u32  //\nwuffs_base__range_ii_u32__unite(wuffs_base__range_ii_u32* r,\n                                wuffs_base__range_ii_u32 s) {\n  if (wuffs_base__range_ii_u32__is_empty(r)) {\n    return s;\n  }\n  if (wuffs_base__range_ii_u32__is_empty(&s)) {\n    return *r;\n  }\n  wuffs_base__range_ii_u32 t;\n  t.min_incl = wuffs_base__u32__min(r->min_incl, s.min_incl);\n  t.max_incl = wuffs_base__u32__max(r->max_incl, s.max_incl);\n  return t;\n}\n\nstatic inline bool  //\nwuffs_base__range_ii_u32__contains(wuffs_base__range_ii_u32* r, uint32_t x) {\n  return (r->min_incl <= x) && (x <= r->max_incl);\n}\n\nstatic inline bool  //\nwuffs_base__range_ii_u32__contains_range(wuffs_base__range_ii_u32* r,\n        " +
-	"                                 wuffs_base__range_ii_u32 s) {\n  return wuffs_base__range_ii_u32__equals(\n      &s, wuffs_base__range_ii_u32__intersect(r, s));\n}\n\n#ifdef __cplusplus\n\ninline bool  //\nwuffs_base__range_ii_u32::is_empty() {\n  return wuffs_base__range_ii_u32__is_empty(this);\n}\n\ninline bool  //\nwuffs_base__range_ii_u32::equals(wuffs_base__range_ii_u32 s) {\n  return wuffs_base__range_ii_u32__equals(this, s);\n}\n\ninline wuffs_base__range_ii_u32  //\nwuffs_base__range_ii_u32::intersect(wuffs_base__range_ii_u32 s) {\n  return wuffs_base__range_ii_u32__intersect(this, s);\n}\n\ninline wuffs_base__range_ii_u32  //\nwuffs_base__range_ii_u32::unite(wuffs_base__range_ii_u32 s) {\n  return wuffs_base__range_ii_u32__unite(this, s);\n}\n\ninline bool  //\nwuffs_base__range_ii_u32::contains(uint32_t x) {\n  return wuffs_base__range_ii_u32__contains(this, x);\n}\n\ninline bool  //\nwuffs_base__range_ii_u32::contains_range(wuffs_base__range_ii_u32 s) {\n  return wuffs_base__range_ii_u32__contains_range(this, s);\n}\n\n#endif  // __c" +
-	"plusplus\n\n" +
+	"d-inclusive\n//\n// For example, with \"ie\", the number of elements in \"uint32_t values in the\n// half-open interval [M, N)\" is equal to max(0, N-M). Furthermore, that number\n// of elements (in one dimension, a length, in two dimensions, a width or\n// height) is itself representable as a uint32_t without overflow, again for\n// uint32_t values M and N. In the contrasting \"ii\" flavor, the length of the\n// closed interval [0, (1<<32) - 1] is 1<<32, which cannot be represented as a\n// uint32_t. In Wuffs, because of this potential overflow, the \"ie\" flavor has\n// length / width / height methods, but the \"ii\" flavor does not.\n//\n// It is valid for min > max (for range_ii) or for min >= max (for range_ie),\n// in which case the range is empty. There are multiple representations of an\n// empty range.\n\ntypedef struct wuffs_base__range_ii_u32__struct {\n  uint32_t min_incl;\n  uint32_t max_incl;\n\n#ifdef __cplusplus\n  inline bool is_empty() const;\n  inline bool equals(wuffs_base__range_ii_u32__struct s) const;\n  inline wuffs_" +
+	"base__range_ii_u32__struct intersect(\n      wuffs_base__range_ii_u32__struct s) const;\n  inline wuffs_base__range_ii_u32__struct unite(\n      wuffs_base__range_ii_u32__struct s) const;\n  inline bool contains(uint32_t x) const;\n  inline bool contains_range(wuffs_base__range_ii_u32__struct s) const;\n#endif  // __cplusplus\n\n} wuffs_base__range_ii_u32;\n\nstatic inline wuffs_base__range_ii_u32  //\nwuffs_base__make_range_ii_u32(uint32_t min_incl, uint32_t max_incl) {\n  wuffs_base__range_ii_u32 ret;\n  ret.min_incl = min_incl;\n  ret.max_incl = max_incl;\n  return ret;\n}\n\nstatic inline bool  //\nwuffs_base__range_ii_u32__is_empty(const wuffs_base__range_ii_u32* r) {\n  return r->min_incl > r->max_incl;\n}\n\nstatic inline bool  //\nwuffs_base__range_ii_u32__equals(const wuffs_base__range_ii_u32* r,\n                                 wuffs_base__range_ii_u32 s) {\n  return (r->min_incl == s.min_incl && r->max_incl == s.max_incl) ||\n         (wuffs_base__range_ii_u32__is_empty(r) &&\n          wuffs_base__range_ii_u32__is_empty(&s)" +
+	");\n}\n\nstatic inline wuffs_base__range_ii_u32  //\nwuffs_base__range_ii_u32__intersect(const wuffs_base__range_ii_u32* r,\n                                    wuffs_base__range_ii_u32 s) {\n  wuffs_base__range_ii_u32 t;\n  t.min_incl = wuffs_base__u32__max(r->min_incl, s.min_incl);\n  t.max_incl = wuffs_base__u32__min(r->max_incl, s.max_incl);\n  return t;\n}\n\nstatic inline wuffs_base__range_ii_u32  //\nwuffs_base__range_ii_u32__unite(const wuffs_base__range_ii_u32* r,\n                                wuffs_base__range_ii_u32 s) {\n  if (wuffs_base__range_ii_u32__is_empty(r)) {\n    return s;\n  }\n  if (wuffs_base__range_ii_u32__is_empty(&s)) {\n    return *r;\n  }\n  wuffs_base__range_ii_u32 t;\n  t.min_incl = wuffs_base__u32__min(r->min_incl, s.min_incl);\n  t.max_incl = wuffs_base__u32__max(r->max_incl, s.max_incl);\n  return t;\n}\n\nstatic inline bool  //\nwuffs_base__range_ii_u32__contains(const wuffs_base__range_ii_u32* r,\n                                   uint32_t x) {\n  return (r->min_incl <= x) && (x <= r->max_incl);\n}\n\n" +
+	"static inline bool  //\nwuffs_base__range_ii_u32__contains_range(const wuffs_base__range_ii_u32* r,\n                                         wuffs_base__range_ii_u32 s) {\n  return wuffs_base__range_ii_u32__equals(\n      &s, wuffs_base__range_ii_u32__intersect(r, s));\n}\n\n#ifdef __cplusplus\n\ninline bool  //\nwuffs_base__range_ii_u32::is_empty() const {\n  return wuffs_base__range_ii_u32__is_empty(this);\n}\n\ninline bool  //\nwuffs_base__range_ii_u32::equals(wuffs_base__range_ii_u32 s) const {\n  return wuffs_base__range_ii_u32__equals(this, s);\n}\n\ninline wuffs_base__range_ii_u32  //\nwuffs_base__range_ii_u32::intersect(wuffs_base__range_ii_u32 s) const {\n  return wuffs_base__range_ii_u32__intersect(this, s);\n}\n\ninline wuffs_base__range_ii_u32  //\nwuffs_base__range_ii_u32::unite(wuffs_base__range_ii_u32 s) const {\n  return wuffs_base__range_ii_u32__unite(this, s);\n}\n\ninline bool  //\nwuffs_base__range_ii_u32::contains(uint32_t x) const {\n  return wuffs_base__range_ii_u32__contains(this, x);\n}\n\ninline bool  //\nwuffs_base_" +
+	"_range_ii_u32::contains_range(wuffs_base__range_ii_u32 s) const {\n  return wuffs_base__range_ii_u32__contains_range(this, s);\n}\n\n#endif  // __cplusplus\n\n" +
 	"" +
-	"// --------\n\ntypedef struct wuffs_base__range_ie_u32__struct {\n  uint32_t min_incl;\n  uint32_t max_excl;\n\n#ifdef __cplusplus\n  inline bool is_empty();\n  inline bool equals(wuffs_base__range_ie_u32__struct s);\n  inline wuffs_base__range_ie_u32__struct intersect(\n      wuffs_base__range_ie_u32__struct s);\n  inline wuffs_base__range_ie_u32__struct unite(\n      wuffs_base__range_ie_u32__struct s);\n  inline bool contains(uint32_t x);\n  inline bool contains_range(wuffs_base__range_ie_u32__struct s);\n  inline uint32_t length();\n#endif  // __cplusplus\n\n} wuffs_base__range_ie_u32;\n\nstatic inline wuffs_base__range_ie_u32  //\nwuffs_base__make_range_ie_u32(uint32_t min_incl, uint32_t max_excl) {\n  wuffs_base__range_ie_u32 ret;\n  ret.min_incl = min_incl;\n  ret.max_excl = max_excl;\n  return ret;\n}\n\nstatic inline bool  //\nwuffs_base__range_ie_u32__is_empty(wuffs_base__range_ie_u32* r) {\n  return r->min_incl >= r->max_excl;\n}\n\nstatic inline bool  //\nwuffs_base__range_ie_u32__equals(wuffs_base__range_ie_u32* r,\n              " +
-	"                   wuffs_base__range_ie_u32 s) {\n  return (r->min_incl == s.min_incl && r->max_excl == s.max_excl) ||\n         (wuffs_base__range_ie_u32__is_empty(r) &&\n          wuffs_base__range_ie_u32__is_empty(&s));\n}\n\nstatic inline wuffs_base__range_ie_u32  //\nwuffs_base__range_ie_u32__intersect(wuffs_base__range_ie_u32* r,\n                                    wuffs_base__range_ie_u32 s) {\n  wuffs_base__range_ie_u32 t;\n  t.min_incl = wuffs_base__u32__max(r->min_incl, s.min_incl);\n  t.max_excl = wuffs_base__u32__min(r->max_excl, s.max_excl);\n  return t;\n}\n\nstatic inline wuffs_base__range_ie_u32  //\nwuffs_base__range_ie_u32__unite(wuffs_base__range_ie_u32* r,\n                                wuffs_base__range_ie_u32 s) {\n  if (wuffs_base__range_ie_u32__is_empty(r)) {\n    return s;\n  }\n  if (wuffs_base__range_ie_u32__is_empty(&s)) {\n    return *r;\n  }\n  wuffs_base__range_ie_u32 t;\n  t.min_incl = wuffs_base__u32__min(r->min_incl, s.min_incl);\n  t.max_excl = wuffs_base__u32__max(r->max_excl, s.max_excl);\n  retu" +
-	"rn t;\n}\n\nstatic inline bool  //\nwuffs_base__range_ie_u32__contains(wuffs_base__range_ie_u32* r, uint32_t x) {\n  return (r->min_incl <= x) && (x < r->max_excl);\n}\n\nstatic inline bool  //\nwuffs_base__range_ie_u32__contains_range(wuffs_base__range_ie_u32* r,\n                                         wuffs_base__range_ie_u32 s) {\n  return wuffs_base__range_ie_u32__equals(\n      &s, wuffs_base__range_ie_u32__intersect(r, s));\n}\n\nstatic inline uint32_t  //\nwuffs_base__range_ie_u32__length(wuffs_base__range_ie_u32* r) {\n  return wuffs_base__u32__sat_sub(r->max_excl, r->min_incl);\n}\n\n#ifdef __cplusplus\n\ninline bool  //\nwuffs_base__range_ie_u32::is_empty() {\n  return wuffs_base__range_ie_u32__is_empty(this);\n}\n\ninline bool  //\nwuffs_base__range_ie_u32::equals(wuffs_base__range_ie_u32 s) {\n  return wuffs_base__range_ie_u32__equals(this, s);\n}\n\ninline wuffs_base__range_ie_u32  //\nwuffs_base__range_ie_u32::intersect(wuffs_base__range_ie_u32 s) {\n  return wuffs_base__range_ie_u32__intersect(this, s);\n}\n\ninline wuffs_base__" +
-	"range_ie_u32  //\nwuffs_base__range_ie_u32::unite(wuffs_base__range_ie_u32 s) {\n  return wuffs_base__range_ie_u32__unite(this, s);\n}\n\ninline bool  //\nwuffs_base__range_ie_u32::contains(uint32_t x) {\n  return wuffs_base__range_ie_u32__contains(this, x);\n}\n\ninline bool  //\nwuffs_base__range_ie_u32::contains_range(wuffs_base__range_ie_u32 s) {\n  return wuffs_base__range_ie_u32__contains_range(this, s);\n}\n\ninline uint32_t  //\nwuffs_base__range_ie_u32::length() {\n  return wuffs_base__range_ie_u32__length(this);\n}\n\n#endif  // __cplusplus\n\n" +
+	"// --------\n\ntypedef struct wuffs_base__range_ie_u32__struct {\n  uint32_t min_incl;\n  uint32_t max_excl;\n\n#ifdef __cplusplus\n  inline bool is_empty() const;\n  inline bool equals(wuffs_base__range_ie_u32__struct s) const;\n  inline wuffs_base__range_ie_u32__struct intersect(\n      wuffs_base__range_ie_u32__struct s) const;\n  inline wuffs_base__range_ie_u32__struct unite(\n      wuffs_base__range_ie_u32__struct s) const;\n  inline bool contains(uint32_t x) const;\n  inline bool contains_range(wuffs_base__range_ie_u32__struct s) const;\n  inline uint32_t length() const;\n#endif  // __cplusplus\n\n} wuffs_base__range_ie_u32;\n\nstatic inline wuffs_base__range_ie_u32  //\nwuffs_base__make_range_ie_u32(uint32_t min_incl, uint32_t max_excl) {\n  wuffs_base__range_ie_u32 ret;\n  ret.min_incl = min_incl;\n  ret.max_excl = max_excl;\n  return ret;\n}\n\nstatic inline bool  //\nwuffs_base__range_ie_u32__is_empty(const wuffs_base__range_ie_u32* r) {\n  return r->min_incl >= r->max_excl;\n}\n\nstatic inline bool  //\nwuffs_base__range_ie_u32__eq" +
+	"uals(const wuffs_base__range_ie_u32* r,\n                                 wuffs_base__range_ie_u32 s) {\n  return (r->min_incl == s.min_incl && r->max_excl == s.max_excl) ||\n         (wuffs_base__range_ie_u32__is_empty(r) &&\n          wuffs_base__range_ie_u32__is_empty(&s));\n}\n\nstatic inline wuffs_base__range_ie_u32  //\nwuffs_base__range_ie_u32__intersect(const wuffs_base__range_ie_u32* r,\n                                    wuffs_base__range_ie_u32 s) {\n  wuffs_base__range_ie_u32 t;\n  t.min_incl = wuffs_base__u32__max(r->min_incl, s.min_incl);\n  t.max_excl = wuffs_base__u32__min(r->max_excl, s.max_excl);\n  return t;\n}\n\nstatic inline wuffs_base__range_ie_u32  //\nwuffs_base__range_ie_u32__unite(const wuffs_base__range_ie_u32* r,\n                                wuffs_base__range_ie_u32 s) {\n  if (wuffs_base__range_ie_u32__is_empty(r)) {\n    return s;\n  }\n  if (wuffs_base__range_ie_u32__is_empty(&s)) {\n    return *r;\n  }\n  wuffs_base__range_ie_u32 t;\n  t.min_incl = wuffs_base__u32__min(r->min_incl, s.min_incl);\n  " +
+	"t.max_excl = wuffs_base__u32__max(r->max_excl, s.max_excl);\n  return t;\n}\n\nstatic inline bool  //\nwuffs_base__range_ie_u32__contains(const wuffs_base__range_ie_u32* r,\n                                   uint32_t x) {\n  return (r->min_incl <= x) && (x < r->max_excl);\n}\n\nstatic inline bool  //\nwuffs_base__range_ie_u32__contains_range(const wuffs_base__range_ie_u32* r,\n                                         wuffs_base__range_ie_u32 s) {\n  return wuffs_base__range_ie_u32__equals(\n      &s, wuffs_base__range_ie_u32__intersect(r, s));\n}\n\nstatic inline uint32_t  //\nwuffs_base__range_ie_u32__length(const wuffs_base__range_ie_u32* r) {\n  return wuffs_base__u32__sat_sub(r->max_excl, r->min_incl);\n}\n\n#ifdef __cplusplus\n\ninline bool  //\nwuffs_base__range_ie_u32::is_empty() const {\n  return wuffs_base__range_ie_u32__is_empty(this);\n}\n\ninline bool  //\nwuffs_base__range_ie_u32::equals(wuffs_base__range_ie_u32 s) const {\n  return wuffs_base__range_ie_u32__equals(this, s);\n}\n\ninline wuffs_base__range_ie_u32  //\nwuffs_base__" +
+	"range_ie_u32::intersect(wuffs_base__range_ie_u32 s) const {\n  return wuffs_base__range_ie_u32__intersect(this, s);\n}\n\ninline wuffs_base__range_ie_u32  //\nwuffs_base__range_ie_u32::unite(wuffs_base__range_ie_u32 s) const {\n  return wuffs_base__range_ie_u32__unite(this, s);\n}\n\ninline bool  //\nwuffs_base__range_ie_u32::contains(uint32_t x) const {\n  return wuffs_base__range_ie_u32__contains(this, x);\n}\n\ninline bool  //\nwuffs_base__range_ie_u32::contains_range(wuffs_base__range_ie_u32 s) const {\n  return wuffs_base__range_ie_u32__contains_range(this, s);\n}\n\ninline uint32_t  //\nwuffs_base__range_ie_u32::length() const {\n  return wuffs_base__range_ie_u32__length(this);\n}\n\n#endif  // __cplusplus\n\n" +
 	"" +
-	"// --------\n\ntypedef struct wuffs_base__range_ii_u64__struct {\n  uint64_t min_incl;\n  uint64_t max_incl;\n\n#ifdef __cplusplus\n  inline bool is_empty();\n  inline bool equals(wuffs_base__range_ii_u64__struct s);\n  inline wuffs_base__range_ii_u64__struct intersect(\n      wuffs_base__range_ii_u64__struct s);\n  inline wuffs_base__range_ii_u64__struct unite(\n      wuffs_base__range_ii_u64__struct s);\n  inline bool contains(uint64_t x);\n  inline bool contains_range(wuffs_base__range_ii_u64__struct s);\n#endif  // __cplusplus\n\n} wuffs_base__range_ii_u64;\n\nstatic inline wuffs_base__range_ii_u64  //\nwuffs_base__make_range_ii_u64(uint64_t min_incl, uint64_t max_incl) {\n  wuffs_base__range_ii_u64 ret;\n  ret.min_incl = min_incl;\n  ret.max_incl = max_incl;\n  return ret;\n}\n\nstatic inline bool  //\nwuffs_base__range_ii_u64__is_empty(wuffs_base__range_ii_u64* r) {\n  return r->min_incl > r->max_incl;\n}\n\nstatic inline bool  //\nwuffs_base__range_ii_u64__equals(wuffs_base__range_ii_u64* r,\n                                 wuffs_base" +
-	"__range_ii_u64 s) {\n  return (r->min_incl == s.min_incl && r->max_incl == s.max_incl) ||\n         (wuffs_base__range_ii_u64__is_empty(r) &&\n          wuffs_base__range_ii_u64__is_empty(&s));\n}\n\nstatic inline wuffs_base__range_ii_u64  //\nwuffs_base__range_ii_u64__intersect(wuffs_base__range_ii_u64* r,\n                                    wuffs_base__range_ii_u64 s) {\n  wuffs_base__range_ii_u64 t;\n  t.min_incl = wuffs_base__u64__max(r->min_incl, s.min_incl);\n  t.max_incl = wuffs_base__u64__min(r->max_incl, s.max_incl);\n  return t;\n}\n\nstatic inline wuffs_base__range_ii_u64  //\nwuffs_base__range_ii_u64__unite(wuffs_base__range_ii_u64* r,\n                                wuffs_base__range_ii_u64 s) {\n  if (wuffs_base__range_ii_u64__is_empty(r)) {\n    return s;\n  }\n  if (wuffs_base__range_ii_u64__is_empty(&s)) {\n    return *r;\n  }\n  wuffs_base__range_ii_u64 t;\n  t.min_incl = wuffs_base__u64__min(r->min_incl, s.min_incl);\n  t.max_incl = wuffs_base__u64__max(r->max_incl, s.max_incl);\n  return t;\n}\n\nstatic inline bool  " +
-	"//\nwuffs_base__range_ii_u64__contains(wuffs_base__range_ii_u64* r, uint64_t x) {\n  return (r->min_incl <= x) && (x <= r->max_incl);\n}\n\nstatic inline bool  //\nwuffs_base__range_ii_u64__contains_range(wuffs_base__range_ii_u64* r,\n                                         wuffs_base__range_ii_u64 s) {\n  return wuffs_base__range_ii_u64__equals(\n      &s, wuffs_base__range_ii_u64__intersect(r, s));\n}\n\n#ifdef __cplusplus\n\ninline bool  //\nwuffs_base__range_ii_u64::is_empty() {\n  return wuffs_base__range_ii_u64__is_empty(this);\n}\n\ninline bool  //\nwuffs_base__range_ii_u64::equals(wuffs_base__range_ii_u64 s) {\n  return wuffs_base__range_ii_u64__equals(this, s);\n}\n\ninline wuffs_base__range_ii_u64  //\nwuffs_base__range_ii_u64::intersect(wuffs_base__range_ii_u64 s) {\n  return wuffs_base__range_ii_u64__intersect(this, s);\n}\n\ninline wuffs_base__range_ii_u64  //\nwuffs_base__range_ii_u64::unite(wuffs_base__range_ii_u64 s) {\n  return wuffs_base__range_ii_u64__unite(this, s);\n}\n\ninline bool  //\nwuffs_base__range_ii_u64::contains" +
-	"(uint64_t x) {\n  return wuffs_base__range_ii_u64__contains(this, x);\n}\n\ninline bool  //\nwuffs_base__range_ii_u64::contains_range(wuffs_base__range_ii_u64 s) {\n  return wuffs_base__range_ii_u64__contains_range(this, s);\n}\n\n#endif  // __cplusplus\n\n" +
+	"// --------\n\ntypedef struct wuffs_base__range_ii_u64__struct {\n  uint64_t min_incl;\n  uint64_t max_incl;\n\n#ifdef __cplusplus\n  inline bool is_empty() const;\n  inline bool equals(wuffs_base__range_ii_u64__struct s) const;\n  inline wuffs_base__range_ii_u64__struct intersect(\n      wuffs_base__range_ii_u64__struct s) const;\n  inline wuffs_base__range_ii_u64__struct unite(\n      wuffs_base__range_ii_u64__struct s) const;\n  inline bool contains(uint64_t x) const;\n  inline bool contains_range(wuffs_base__range_ii_u64__struct s) const;\n#endif  // __cplusplus\n\n} wuffs_base__range_ii_u64;\n\nstatic inline wuffs_base__range_ii_u64  //\nwuffs_base__make_range_ii_u64(uint64_t min_incl, uint64_t max_incl) {\n  wuffs_base__range_ii_u64 ret;\n  ret.min_incl = min_incl;\n  ret.max_incl = max_incl;\n  return ret;\n}\n\nstatic inline bool  //\nwuffs_base__range_ii_u64__is_empty(const wuffs_base__range_ii_u64* r) {\n  return r->min_incl > r->max_incl;\n}\n\nstatic inline bool  //\nwuffs_base__range_ii_u64__equals(const wuffs_base__range_ii_u64" +
+	"* r,\n                                 wuffs_base__range_ii_u64 s) {\n  return (r->min_incl == s.min_incl && r->max_incl == s.max_incl) ||\n         (wuffs_base__range_ii_u64__is_empty(r) &&\n          wuffs_base__range_ii_u64__is_empty(&s));\n}\n\nstatic inline wuffs_base__range_ii_u64  //\nwuffs_base__range_ii_u64__intersect(const wuffs_base__range_ii_u64* r,\n                                    wuffs_base__range_ii_u64 s) {\n  wuffs_base__range_ii_u64 t;\n  t.min_incl = wuffs_base__u64__max(r->min_incl, s.min_incl);\n  t.max_incl = wuffs_base__u64__min(r->max_incl, s.max_incl);\n  return t;\n}\n\nstatic inline wuffs_base__range_ii_u64  //\nwuffs_base__range_ii_u64__unite(const wuffs_base__range_ii_u64* r,\n                                wuffs_base__range_ii_u64 s) {\n  if (wuffs_base__range_ii_u64__is_empty(r)) {\n    return s;\n  }\n  if (wuffs_base__range_ii_u64__is_empty(&s)) {\n    return *r;\n  }\n  wuffs_base__range_ii_u64 t;\n  t.min_incl = wuffs_base__u64__min(r->min_incl, s.min_incl);\n  t.max_incl = wuffs_base__u64__max(r" +
+	"->max_incl, s.max_incl);\n  return t;\n}\n\nstatic inline bool  //\nwuffs_base__range_ii_u64__contains(const wuffs_base__range_ii_u64* r,\n                                   uint64_t x) {\n  return (r->min_incl <= x) && (x <= r->max_incl);\n}\n\nstatic inline bool  //\nwuffs_base__range_ii_u64__contains_range(const wuffs_base__range_ii_u64* r,\n                                         wuffs_base__range_ii_u64 s) {\n  return wuffs_base__range_ii_u64__equals(\n      &s, wuffs_base__range_ii_u64__intersect(r, s));\n}\n\n#ifdef __cplusplus\n\ninline bool  //\nwuffs_base__range_ii_u64::is_empty() const {\n  return wuffs_base__range_ii_u64__is_empty(this);\n}\n\ninline bool  //\nwuffs_base__range_ii_u64::equals(wuffs_base__range_ii_u64 s) const {\n  return wuffs_base__range_ii_u64__equals(this, s);\n}\n\ninline wuffs_base__range_ii_u64  //\nwuffs_base__range_ii_u64::intersect(wuffs_base__range_ii_u64 s) const {\n  return wuffs_base__range_ii_u64__intersect(this, s);\n}\n\ninline wuffs_base__range_ii_u64  //\nwuffs_base__range_ii_u64::unite(wuffs_bas" +
+	"e__range_ii_u64 s) const {\n  return wuffs_base__range_ii_u64__unite(this, s);\n}\n\ninline bool  //\nwuffs_base__range_ii_u64::contains(uint64_t x) const {\n  return wuffs_base__range_ii_u64__contains(this, x);\n}\n\ninline bool  //\nwuffs_base__range_ii_u64::contains_range(wuffs_base__range_ii_u64 s) const {\n  return wuffs_base__range_ii_u64__contains_range(this, s);\n}\n\n#endif  // __cplusplus\n\n" +
 	"" +
-	"// --------\n\ntypedef struct wuffs_base__range_ie_u64__struct {\n  uint64_t min_incl;\n  uint64_t max_excl;\n\n#ifdef __cplusplus\n  inline bool is_empty();\n  inline bool equals(wuffs_base__range_ie_u64__struct s);\n  inline wuffs_base__range_ie_u64__struct intersect(\n      wuffs_base__range_ie_u64__struct s);\n  inline wuffs_base__range_ie_u64__struct unite(\n      wuffs_base__range_ie_u64__struct s);\n  inline bool contains(uint64_t x);\n  inline bool contains_range(wuffs_base__range_ie_u64__struct s);\n  inline uint64_t length();\n#endif  // __cplusplus\n\n} wuffs_base__range_ie_u64;\n\nstatic inline wuffs_base__range_ie_u64  //\nwuffs_base__make_range_ie_u64(uint64_t min_incl, uint64_t max_excl) {\n  wuffs_base__range_ie_u64 ret;\n  ret.min_incl = min_incl;\n  ret.max_excl = max_excl;\n  return ret;\n}\n\nstatic inline bool  //\nwuffs_base__range_ie_u64__is_empty(wuffs_base__range_ie_u64* r) {\n  return r->min_incl >= r->max_excl;\n}\n\nstatic inline bool  //\nwuffs_base__range_ie_u64__equals(wuffs_base__range_ie_u64* r,\n              " +
-	"                   wuffs_base__range_ie_u64 s) {\n  return (r->min_incl == s.min_incl && r->max_excl == s.max_excl) ||\n         (wuffs_base__range_ie_u64__is_empty(r) &&\n          wuffs_base__range_ie_u64__is_empty(&s));\n}\n\nstatic inline wuffs_base__range_ie_u64  //\nwuffs_base__range_ie_u64__intersect(wuffs_base__range_ie_u64* r,\n                                    wuffs_base__range_ie_u64 s) {\n  wuffs_base__range_ie_u64 t;\n  t.min_incl = wuffs_base__u64__max(r->min_incl, s.min_incl);\n  t.max_excl = wuffs_base__u64__min(r->max_excl, s.max_excl);\n  return t;\n}\n\nstatic inline wuffs_base__range_ie_u64  //\nwuffs_base__range_ie_u64__unite(wuffs_base__range_ie_u64* r,\n                                wuffs_base__range_ie_u64 s) {\n  if (wuffs_base__range_ie_u64__is_empty(r)) {\n    return s;\n  }\n  if (wuffs_base__range_ie_u64__is_empty(&s)) {\n    return *r;\n  }\n  wuffs_base__range_ie_u64 t;\n  t.min_incl = wuffs_base__u64__min(r->min_incl, s.min_incl);\n  t.max_excl = wuffs_base__u64__max(r->max_excl, s.max_excl);\n  retu" +
-	"rn t;\n}\n\nstatic inline bool  //\nwuffs_base__range_ie_u64__contains(wuffs_base__range_ie_u64* r, uint64_t x) {\n  return (r->min_incl <= x) && (x < r->max_excl);\n}\n\nstatic inline bool  //\nwuffs_base__range_ie_u64__contains_range(wuffs_base__range_ie_u64* r,\n                                         wuffs_base__range_ie_u64 s) {\n  return wuffs_base__range_ie_u64__equals(\n      &s, wuffs_base__range_ie_u64__intersect(r, s));\n}\n\nstatic inline uint64_t  //\nwuffs_base__range_ie_u64__length(wuffs_base__range_ie_u64* r) {\n  return wuffs_base__u64__sat_sub(r->max_excl, r->min_incl);\n}\n\n#ifdef __cplusplus\n\ninline bool  //\nwuffs_base__range_ie_u64::is_empty() {\n  return wuffs_base__range_ie_u64__is_empty(this);\n}\n\ninline bool  //\nwuffs_base__range_ie_u64::equals(wuffs_base__range_ie_u64 s) {\n  return wuffs_base__range_ie_u64__equals(this, s);\n}\n\ninline wuffs_base__range_ie_u64  //\nwuffs_base__range_ie_u64::intersect(wuffs_base__range_ie_u64 s) {\n  return wuffs_base__range_ie_u64__intersect(this, s);\n}\n\ninline wuffs_base__" +
-	"range_ie_u64  //\nwuffs_base__range_ie_u64::unite(wuffs_base__range_ie_u64 s) {\n  return wuffs_base__range_ie_u64__unite(this, s);\n}\n\ninline bool  //\nwuffs_base__range_ie_u64::contains(uint64_t x) {\n  return wuffs_base__range_ie_u64__contains(this, x);\n}\n\ninline bool  //\nwuffs_base__range_ie_u64::contains_range(wuffs_base__range_ie_u64 s) {\n  return wuffs_base__range_ie_u64__contains_range(this, s);\n}\n\ninline uint64_t  //\nwuffs_base__range_ie_u64::length() {\n  return wuffs_base__range_ie_u64__length(this);\n}\n\n#endif  // __cplusplus\n\n" +
+	"// --------\n\ntypedef struct wuffs_base__range_ie_u64__struct {\n  uint64_t min_incl;\n  uint64_t max_excl;\n\n#ifdef __cplusplus\n  inline bool is_empty() const;\n  inline bool equals(wuffs_base__range_ie_u64__struct s) const;\n  inline wuffs_base__range_ie_u64__struct intersect(\n      wuffs_base__range_ie_u64__struct s) const;\n  inline wuffs_base__range_ie_u64__struct unite(\n      wuffs_base__range_ie_u64__struct s) const;\n  inline bool contains(uint64_t x) const;\n  inline bool contains_range(wuffs_base__range_ie_u64__struct s) const;\n  inline uint64_t length() const;\n#endif  // __cplusplus\n\n} wuffs_base__range_ie_u64;\n\nstatic inline wuffs_base__range_ie_u64  //\nwuffs_base__make_range_ie_u64(uint64_t min_incl, uint64_t max_excl) {\n  wuffs_base__range_ie_u64 ret;\n  ret.min_incl = min_incl;\n  ret.max_excl = max_excl;\n  return ret;\n}\n\nstatic inline bool  //\nwuffs_base__range_ie_u64__is_empty(const wuffs_base__range_ie_u64* r) {\n  return r->min_incl >= r->max_excl;\n}\n\nstatic inline bool  //\nwuffs_base__range_ie_u64__eq" +
+	"uals(const wuffs_base__range_ie_u64* r,\n                                 wuffs_base__range_ie_u64 s) {\n  return (r->min_incl == s.min_incl && r->max_excl == s.max_excl) ||\n         (wuffs_base__range_ie_u64__is_empty(r) &&\n          wuffs_base__range_ie_u64__is_empty(&s));\n}\n\nstatic inline wuffs_base__range_ie_u64  //\nwuffs_base__range_ie_u64__intersect(const wuffs_base__range_ie_u64* r,\n                                    wuffs_base__range_ie_u64 s) {\n  wuffs_base__range_ie_u64 t;\n  t.min_incl = wuffs_base__u64__max(r->min_incl, s.min_incl);\n  t.max_excl = wuffs_base__u64__min(r->max_excl, s.max_excl);\n  return t;\n}\n\nstatic inline wuffs_base__range_ie_u64  //\nwuffs_base__range_ie_u64__unite(const wuffs_base__range_ie_u64* r,\n                                wuffs_base__range_ie_u64 s) {\n  if (wuffs_base__range_ie_u64__is_empty(r)) {\n    return s;\n  }\n  if (wuffs_base__range_ie_u64__is_empty(&s)) {\n    return *r;\n  }\n  wuffs_base__range_ie_u64 t;\n  t.min_incl = wuffs_base__u64__min(r->min_incl, s.min_incl);\n  " +
+	"t.max_excl = wuffs_base__u64__max(r->max_excl, s.max_excl);\n  return t;\n}\n\nstatic inline bool  //\nwuffs_base__range_ie_u64__contains(const wuffs_base__range_ie_u64* r,\n                                   uint64_t x) {\n  return (r->min_incl <= x) && (x < r->max_excl);\n}\n\nstatic inline bool  //\nwuffs_base__range_ie_u64__contains_range(const wuffs_base__range_ie_u64* r,\n                                         wuffs_base__range_ie_u64 s) {\n  return wuffs_base__range_ie_u64__equals(\n      &s, wuffs_base__range_ie_u64__intersect(r, s));\n}\n\nstatic inline uint64_t  //\nwuffs_base__range_ie_u64__length(const wuffs_base__range_ie_u64* r) {\n  return wuffs_base__u64__sat_sub(r->max_excl, r->min_incl);\n}\n\n#ifdef __cplusplus\n\ninline bool  //\nwuffs_base__range_ie_u64::is_empty() const {\n  return wuffs_base__range_ie_u64__is_empty(this);\n}\n\ninline bool  //\nwuffs_base__range_ie_u64::equals(wuffs_base__range_ie_u64 s) const {\n  return wuffs_base__range_ie_u64__equals(this, s);\n}\n\ninline wuffs_base__range_ie_u64  //\nwuffs_base__" +
+	"range_ie_u64::intersect(wuffs_base__range_ie_u64 s) const {\n  return wuffs_base__range_ie_u64__intersect(this, s);\n}\n\ninline wuffs_base__range_ie_u64  //\nwuffs_base__range_ie_u64::unite(wuffs_base__range_ie_u64 s) const {\n  return wuffs_base__range_ie_u64__unite(this, s);\n}\n\ninline bool  //\nwuffs_base__range_ie_u64::contains(uint64_t x) const {\n  return wuffs_base__range_ie_u64__contains(this, x);\n}\n\ninline bool  //\nwuffs_base__range_ie_u64::contains_range(wuffs_base__range_ie_u64 s) const {\n  return wuffs_base__range_ie_u64__contains_range(this, s);\n}\n\ninline uint64_t  //\nwuffs_base__range_ie_u64::length() const {\n  return wuffs_base__range_ie_u64__length(this);\n}\n\n#endif  // __cplusplus\n\n" +
 	"" +
-	"// --------\n\n// wuffs_base__rect_ii_u32 is a rectangle (a 2-dimensional range) on the\n// integer grid. The \"ii\" means that the bounds are inclusive on the low end\n// and inclusive on the high end. It contains all points (x, y) such that\n// ((min_incl_x <= x) && (x <= max_incl_x)) and likewise for y.\n//\n// It is valid for min > max, in which case the rectangle is empty. There are\n// multiple representations of an empty rectangle.\n//\n// The X and Y axes increase right and down.\ntypedef struct wuffs_base__rect_ii_u32__struct {\n  uint32_t min_incl_x;\n  uint32_t min_incl_y;\n  uint32_t max_incl_x;\n  uint32_t max_incl_y;\n\n#ifdef __cplusplus\n  inline bool is_empty();\n  inline bool equals(wuffs_base__rect_ii_u32__struct s);\n  inline wuffs_base__rect_ii_u32__struct intersect(\n      wuffs_base__rect_ii_u32__struct s);\n  inline wuffs_base__rect_ii_u32__struct unite(\n      wuffs_base__rect_ii_u32__struct s);\n  inline bool contains(uint32_t x, uint32_t y);\n  inline bool contains_rect(wuffs_base__rect_ii_u32__struct s);\n#en" +
-	"dif  // __cplusplus\n\n} wuffs_base__rect_ii_u32;\n\nstatic inline wuffs_base__rect_ii_u32  //\nwuffs_base__make_rect_ii_u32(uint32_t min_incl_x,\n                             uint32_t min_incl_y,\n                             uint32_t max_incl_x,\n                             uint32_t max_incl_y) {\n  wuffs_base__rect_ii_u32 ret;\n  ret.min_incl_x = min_incl_x;\n  ret.min_incl_y = min_incl_y;\n  ret.max_incl_x = max_incl_x;\n  ret.max_incl_y = max_incl_y;\n  return ret;\n}\n\nstatic inline bool  //\nwuffs_base__rect_ii_u32__is_empty(wuffs_base__rect_ii_u32* r) {\n  return (r->min_incl_x > r->max_incl_x) || (r->min_incl_y > r->max_incl_y);\n}\n\nstatic inline bool  //\nwuffs_base__rect_ii_u32__equals(wuffs_base__rect_ii_u32* r,\n                                wuffs_base__rect_ii_u32 s) {\n  return (r->min_incl_x == s.min_incl_x && r->min_incl_y == s.min_incl_y &&\n          r->max_incl_x == s.max_incl_x && r->max_incl_y == s.max_incl_y) ||\n         (wuffs_base__rect_ii_u32__is_empty(r) &&\n          wuffs_base__rect_ii_u32__is_empty(&" +
-	"s));\n}\n\nstatic inline wuffs_base__rect_ii_u32  //\nwuffs_base__rect_ii_u32__intersect(wuffs_base__rect_ii_u32* r,\n                                   wuffs_base__rect_ii_u32 s) {\n  wuffs_base__rect_ii_u32 t;\n  t.min_incl_x = wuffs_base__u32__max(r->min_incl_x, s.min_incl_x);\n  t.min_incl_y = wuffs_base__u32__max(r->min_incl_y, s.min_incl_y);\n  t.max_incl_x = wuffs_base__u32__min(r->max_incl_x, s.max_incl_x);\n  t.max_incl_y = wuffs_base__u32__min(r->max_incl_y, s.max_incl_y);\n  return t;\n}\n\nstatic inline wuffs_base__rect_ii_u32  //\nwuffs_base__rect_ii_u32__unite(wuffs_base__rect_ii_u32* r,\n                               wuffs_base__rect_ii_u32 s) {\n  if (wuffs_base__rect_ii_u32__is_empty(r)) {\n    return s;\n  }\n  if (wuffs_base__rect_ii_u32__is_empty(&s)) {\n    return *r;\n  }\n  wuffs_base__rect_ii_u32 t;\n  t.min_incl_x = wuffs_base__u32__min(r->min_incl_x, s.min_incl_x);\n  t.min_incl_y = wuffs_base__u32__min(r->min_incl_y, s.min_incl_y);\n  t.max_incl_x = wuffs_base__u32__max(r->max_incl_x, s.max_incl_x);\n  t.max" +
-	"_incl_y = wuffs_base__u32__max(r->max_incl_y, s.max_incl_y);\n  return t;\n}\n\nstatic inline bool  //\nwuffs_base__rect_ii_u32__contains(wuffs_base__rect_ii_u32* r,\n                                  uint32_t x,\n                                  uint32_t y) {\n  return (r->min_incl_x <= x) && (x <= r->max_incl_x) && (r->min_incl_y <= y) &&\n         (y <= r->max_incl_y);\n}\n\nstatic inline bool  //\nwuffs_base__rect_ii_u32__contains_rect(wuffs_base__rect_ii_u32* r,\n                                       wuffs_base__rect_ii_u32 s) {\n  return wuffs_base__rect_ii_u32__equals(\n      &s, wuffs_base__rect_ii_u32__intersect(r, s));\n}\n\n#ifdef __cplusplus\n\ninline bool  //\nwuffs_base__rect_ii_u32::is_empty() {\n  return wuffs_base__rect_ii_u32__is_empty(this);\n}\n\ninline bool  //\nwuffs_base__rect_ii_u32::equals(wuffs_base__rect_ii_u32 s) {\n  return wuffs_base__rect_ii_u32__equals(this, s);\n}\n\ninline wuffs_base__rect_ii_u32  //\nwuffs_base__rect_ii_u32::intersect(wuffs_base__rect_ii_u32 s) {\n  return wuffs_base__rect_ii_u32__interse" +
-	"ct(this, s);\n}\n\ninline wuffs_base__rect_ii_u32  //\nwuffs_base__rect_ii_u32::unite(wuffs_base__rect_ii_u32 s) {\n  return wuffs_base__rect_ii_u32__unite(this, s);\n}\n\ninline bool  //\nwuffs_base__rect_ii_u32::contains(uint32_t x, uint32_t y) {\n  return wuffs_base__rect_ii_u32__contains(this, x, y);\n}\n\ninline bool  //\nwuffs_base__rect_ii_u32::contains_rect(wuffs_base__rect_ii_u32 s) {\n  return wuffs_base__rect_ii_u32__contains_rect(this, s);\n}\n\n#endif  // __cplusplus\n\n" +
+	"// --------\n\n// wuffs_base__rect_ii_u32 is a rectangle (a 2-dimensional range) on the\n// integer grid. The \"ii\" means that the bounds are inclusive on the low end\n// and inclusive on the high end. It contains all points (x, y) such that\n// ((min_incl_x <= x) && (x <= max_incl_x)) and likewise for y.\n//\n// It is valid for min > max, in which case the rectangle is empty. There are\n// multiple representations of an empty rectangle.\n//\n// The X and Y axes increase right and down.\ntypedef struct wuffs_base__rect_ii_u32__struct {\n  uint32_t min_incl_x;\n  uint32_t min_incl_y;\n  uint32_t max_incl_x;\n  uint32_t max_incl_y;\n\n#ifdef __cplusplus\n  inline bool is_empty() const;\n  inline bool equals(wuffs_base__rect_ii_u32__struct s) const;\n  inline wuffs_base__rect_ii_u32__struct intersect(\n      wuffs_base__rect_ii_u32__struct s) const;\n  inline wuffs_base__rect_ii_u32__struct unite(\n      wuffs_base__rect_ii_u32__struct s) const;\n  inline bool contains(uint32_t x, uint32_t y) const;\n  inline bool contains_rect(wuffs_bas" +
+	"e__rect_ii_u32__struct s) const;\n#endif  // __cplusplus\n\n} wuffs_base__rect_ii_u32;\n\nstatic inline wuffs_base__rect_ii_u32  //\nwuffs_base__make_rect_ii_u32(uint32_t min_incl_x,\n                             uint32_t min_incl_y,\n                             uint32_t max_incl_x,\n                             uint32_t max_incl_y) {\n  wuffs_base__rect_ii_u32 ret;\n  ret.min_incl_x = min_incl_x;\n  ret.min_incl_y = min_incl_y;\n  ret.max_incl_x = max_incl_x;\n  ret.max_incl_y = max_incl_y;\n  return ret;\n}\n\nstatic inline bool  //\nwuffs_base__rect_ii_u32__is_empty(const wuffs_base__rect_ii_u32* r) {\n  return (r->min_incl_x > r->max_incl_x) || (r->min_incl_y > r->max_incl_y);\n}\n\nstatic inline bool  //\nwuffs_base__rect_ii_u32__equals(const wuffs_base__rect_ii_u32* r,\n                                wuffs_base__rect_ii_u32 s) {\n  return (r->min_incl_x == s.min_incl_x && r->min_incl_y == s.min_incl_y &&\n          r->max_incl_x == s.max_incl_x && r->max_incl_y == s.max_incl_y) ||\n         (wuffs_base__rect_ii_u32__is_empty(r) " +
+	"&&\n          wuffs_base__rect_ii_u32__is_empty(&s));\n}\n\nstatic inline wuffs_base__rect_ii_u32  //\nwuffs_base__rect_ii_u32__intersect(const wuffs_base__rect_ii_u32* r,\n                                   wuffs_base__rect_ii_u32 s) {\n  wuffs_base__rect_ii_u32 t;\n  t.min_incl_x = wuffs_base__u32__max(r->min_incl_x, s.min_incl_x);\n  t.min_incl_y = wuffs_base__u32__max(r->min_incl_y, s.min_incl_y);\n  t.max_incl_x = wuffs_base__u32__min(r->max_incl_x, s.max_incl_x);\n  t.max_incl_y = wuffs_base__u32__min(r->max_incl_y, s.max_incl_y);\n  return t;\n}\n\nstatic inline wuffs_base__rect_ii_u32  //\nwuffs_base__rect_ii_u32__unite(const wuffs_base__rect_ii_u32* r,\n                               wuffs_base__rect_ii_u32 s) {\n  if (wuffs_base__rect_ii_u32__is_empty(r)) {\n    return s;\n  }\n  if (wuffs_base__rect_ii_u32__is_empty(&s)) {\n    return *r;\n  }\n  wuffs_base__rect_ii_u32 t;\n  t.min_incl_x = wuffs_base__u32__min(r->min_incl_x, s.min_incl_x);\n  t.min_incl_y = wuffs_base__u32__min(r->min_incl_y, s.min_incl_y);\n  t.max_incl_x " +
+	"= wuffs_base__u32__max(r->max_incl_x, s.max_incl_x);\n  t.max_incl_y = wuffs_base__u32__max(r->max_incl_y, s.max_incl_y);\n  return t;\n}\n\nstatic inline bool  //\nwuffs_base__rect_ii_u32__contains(const wuffs_base__rect_ii_u32* r,\n                                  uint32_t x,\n                                  uint32_t y) {\n  return (r->min_incl_x <= x) && (x <= r->max_incl_x) && (r->min_incl_y <= y) &&\n         (y <= r->max_incl_y);\n}\n\nstatic inline bool  //\nwuffs_base__rect_ii_u32__contains_rect(const wuffs_base__rect_ii_u32* r,\n                                       wuffs_base__rect_ii_u32 s) {\n  return wuffs_base__rect_ii_u32__equals(\n      &s, wuffs_base__rect_ii_u32__intersect(r, s));\n}\n\n#ifdef __cplusplus\n\ninline bool  //\nwuffs_base__rect_ii_u32::is_empty() const {\n  return wuffs_base__rect_ii_u32__is_empty(this);\n}\n\ninline bool  //\nwuffs_base__rect_ii_u32::equals(wuffs_base__rect_ii_u32 s) const {\n  return wuffs_base__rect_ii_u32__equals(this, s);\n}\n\ninline wuffs_base__rect_ii_u32  //\nwuffs_base__rect_ii_u" +
+	"32::intersect(wuffs_base__rect_ii_u32 s) const {\n  return wuffs_base__rect_ii_u32__intersect(this, s);\n}\n\ninline wuffs_base__rect_ii_u32  //\nwuffs_base__rect_ii_u32::unite(wuffs_base__rect_ii_u32 s) const {\n  return wuffs_base__rect_ii_u32__unite(this, s);\n}\n\ninline bool  //\nwuffs_base__rect_ii_u32::contains(uint32_t x, uint32_t y) const {\n  return wuffs_base__rect_ii_u32__contains(this, x, y);\n}\n\ninline bool  //\nwuffs_base__rect_ii_u32::contains_rect(wuffs_base__rect_ii_u32 s) const {\n  return wuffs_base__rect_ii_u32__contains_rect(this, s);\n}\n\n#endif  // __cplusplus\n\n" +
 	"" +
-	"// --------\n\n// wuffs_base__rect_ie_u32 is a rectangle (a 2-dimensional range) on the\n// integer grid. The \"ie\" means that the bounds are inclusive on the low end\n// and exclusive on the high end. It contains all points (x, y) such that\n// ((min_incl_x <= x) && (x < max_excl_x)) and likewise for y.\n//\n// It is valid for min >= max, in which case the rectangle is empty. There are\n// multiple representations of an empty rectangle, including a value with all\n// fields zero.\n//\n// The X and Y axes increase right and down.\ntypedef struct wuffs_base__rect_ie_u32__struct {\n  uint32_t min_incl_x;\n  uint32_t min_incl_y;\n  uint32_t max_excl_x;\n  uint32_t max_excl_y;\n\n#ifdef __cplusplus\n  inline bool is_empty();\n  inline bool equals(wuffs_base__rect_ie_u32__struct s);\n  inline wuffs_base__rect_ie_u32__struct intersect(\n      wuffs_base__rect_ie_u32__struct s);\n  inline wuffs_base__rect_ie_u32__struct unite(\n      wuffs_base__rect_ie_u32__struct s);\n  inline bool contains(uint32_t x, uint32_t y);\n  inline bool contains_r" +
-	"ect(wuffs_base__rect_ie_u32__struct s);\n  inline uint32_t width();\n  inline uint32_t height();\n#endif  // __cplusplus\n\n} wuffs_base__rect_ie_u32;\n\nstatic inline wuffs_base__rect_ie_u32  //\nwuffs_base__make_rect_ie_u32(uint32_t min_incl_x,\n                             uint32_t min_incl_y,\n                             uint32_t max_excl_x,\n                             uint32_t max_excl_y) {\n  wuffs_base__rect_ie_u32 ret;\n  ret.min_incl_x = min_incl_x;\n  ret.min_incl_y = min_incl_y;\n  ret.max_excl_x = max_excl_x;\n  ret.max_excl_y = max_excl_y;\n  return ret;\n}\n\nstatic inline bool  //\nwuffs_base__rect_ie_u32__is_empty(wuffs_base__rect_ie_u32* r) {\n  return (r->min_incl_x >= r->max_excl_x) || (r->min_incl_y >= r->max_excl_y);\n}\n\nstatic inline bool  //\nwuffs_base__rect_ie_u32__equals(wuffs_base__rect_ie_u32* r,\n                                wuffs_base__rect_ie_u32 s) {\n  return (r->min_incl_x == s.min_incl_x && r->min_incl_y == s.min_incl_y &&\n          r->max_excl_x == s.max_excl_x && r->max_excl_y == s.max_excl_y" +
-	") ||\n         (wuffs_base__rect_ie_u32__is_empty(r) &&\n          wuffs_base__rect_ie_u32__is_empty(&s));\n}\n\nstatic inline wuffs_base__rect_ie_u32  //\nwuffs_base__rect_ie_u32__intersect(wuffs_base__rect_ie_u32* r,\n                                   wuffs_base__rect_ie_u32 s) {\n  wuffs_base__rect_ie_u32 t;\n  t.min_incl_x = wuffs_base__u32__max(r->min_incl_x, s.min_incl_x);\n  t.min_incl_y = wuffs_base__u32__max(r->min_incl_y, s.min_incl_y);\n  t.max_excl_x = wuffs_base__u32__min(r->max_excl_x, s.max_excl_x);\n  t.max_excl_y = wuffs_base__u32__min(r->max_excl_y, s.max_excl_y);\n  return t;\n}\n\nstatic inline wuffs_base__rect_ie_u32  //\nwuffs_base__rect_ie_u32__unite(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  wuffs_base__rect_ie_u32 t;\n  t.min_incl_x = wuffs_base__u32__min(r->min_incl_x, s.min_incl_x);\n  t.min_incl_y = wuffs_base__u32__min(r->mi" +
-	"n_incl_y, s.min_incl_y);\n  t.max_excl_x = wuffs_base__u32__max(r->max_excl_x, s.max_excl_x);\n  t.max_excl_y = wuffs_base__u32__max(r->max_excl_y, s.max_excl_y);\n  return t;\n}\n\nstatic inline bool  //\nwuffs_base__rect_ie_u32__contains(wuffs_base__rect_ie_u32* r,\n                                  uint32_t x,\n                                  uint32_t y) {\n  return (r->min_incl_x <= x) && (x < r->max_excl_x) && (r->min_incl_y <= y) &&\n         (y < r->max_excl_y);\n}\n\nstatic inline bool  //\nwuffs_base__rect_ie_u32__contains_rect(wuffs_base__rect_ie_u32* r,\n                                       wuffs_base__rect_ie_u32 s) {\n  return wuffs_base__rect_ie_u32__equals(\n      &s, wuffs_base__rect_ie_u32__intersect(r, s));\n}\n\nstatic inline uint32_t  //\nwuffs_base__rect_ie_u32__width(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  //\nwuffs_base__rect_ie_u32__height(wuffs_base__rect_ie_u32* r) {\n  return wuffs_base__u32__sat_sub(r->max_excl_y, r->min" +
-	"_incl_y);\n}\n\n#ifdef __cplusplus\n\ninline bool  //\nwuffs_base__rect_ie_u32::is_empty() {\n  return wuffs_base__rect_ie_u32__is_empty(this);\n}\n\ninline bool  //\nwuffs_base__rect_ie_u32::equals(wuffs_base__rect_ie_u32 s) {\n  return wuffs_base__rect_ie_u32__equals(this, s);\n}\n\ninline wuffs_base__rect_ie_u32  //\nwuffs_base__rect_ie_u32::intersect(wuffs_base__rect_ie_u32 s) {\n  return wuffs_base__rect_ie_u32__intersect(this, s);\n}\n\ninline wuffs_base__rect_ie_u32  //\nwuffs_base__rect_ie_u32::unite(wuffs_base__rect_ie_u32 s) {\n  return wuffs_base__rect_ie_u32__unite(this, s);\n}\n\ninline bool  //\nwuffs_base__rect_ie_u32::contains(uint32_t x, uint32_t y) {\n  return wuffs_base__rect_ie_u32__contains(this, x, y);\n}\n\ninline bool  //\nwuffs_base__rect_ie_u32::contains_rect(wuffs_base__rect_ie_u32 s) {\n  return wuffs_base__rect_ie_u32__contains_rect(this, s);\n}\n\ninline uint32_t  //\nwuffs_base__rect_ie_u32::width() {\n  return wuffs_base__rect_ie_u32__width(this);\n}\n\ninline uint32_t  //\nwuffs_base__rect_ie_u32::height() {\n  return" +
-	" wuffs_base__rect_ie_u32__height(this);\n}\n\n#endif  // __cplusplus\n" +
+	"// --------\n\n// wuffs_base__rect_ie_u32 is a rectangle (a 2-dimensional range) on the\n// integer grid. The \"ie\" means that the bounds are inclusive on the low end\n// and exclusive on the high end. It contains all points (x, y) such that\n// ((min_incl_x <= x) && (x < max_excl_x)) and likewise for y.\n//\n// It is valid for min >= max, in which case the rectangle is empty. There are\n// multiple representations of an empty rectangle, including a value with all\n// fields zero.\n//\n// The X and Y axes increase right and down.\ntypedef struct wuffs_base__rect_ie_u32__struct {\n  uint32_t min_incl_x;\n  uint32_t min_incl_y;\n  uint32_t max_excl_x;\n  uint32_t max_excl_y;\n\n#ifdef __cplusplus\n  inline bool is_empty() const;\n  inline bool equals(wuffs_base__rect_ie_u32__struct s) const;\n  inline wuffs_base__rect_ie_u32__struct intersect(\n      wuffs_base__rect_ie_u32__struct s) const;\n  inline wuffs_base__rect_ie_u32__struct unite(\n      wuffs_base__rect_ie_u32__struct s) const;\n  inline bool contains(uint32_t x, uint32_t y) c" +
+	"onst;\n  inline bool contains_rect(wuffs_base__rect_ie_u32__struct s) const;\n  inline uint32_t width() const;\n  inline uint32_t height() const;\n#endif  // __cplusplus\n\n} wuffs_base__rect_ie_u32;\n\nstatic inline wuffs_base__rect_ie_u32  //\nwuffs_base__make_rect_ie_u32(uint32_t min_incl_x,\n                             uint32_t min_incl_y,\n                             uint32_t max_excl_x,\n                             uint32_t max_excl_y) {\n  wuffs_base__rect_ie_u32 ret;\n  ret.min_incl_x = min_incl_x;\n  ret.min_incl_y = min_incl_y;\n  ret.max_excl_x = max_excl_x;\n  ret.max_excl_y = max_excl_y;\n  return ret;\n}\n\nstatic inline bool  //\nwuffs_base__rect_ie_u32__is_empty(const wuffs_base__rect_ie_u32* r) {\n  return (r->min_incl_x >= r->max_excl_x) || (r->min_incl_y >= r->max_excl_y);\n}\n\nstatic inline bool  //\nwuffs_base__rect_ie_u32__equals(const wuffs_base__rect_ie_u32* r,\n                                wuffs_base__rect_ie_u32 s) {\n  return (r->min_incl_x == s.min_incl_x && r->min_incl_y == s.min_incl_y &&\n          r-" +
+	">max_excl_x == s.max_excl_x && r->max_excl_y == s.max_excl_y) ||\n         (wuffs_base__rect_ie_u32__is_empty(r) &&\n          wuffs_base__rect_ie_u32__is_empty(&s));\n}\n\nstatic inline wuffs_base__rect_ie_u32  //\nwuffs_base__rect_ie_u32__intersect(const wuffs_base__rect_ie_u32* r,\n                                   wuffs_base__rect_ie_u32 s) {\n  wuffs_base__rect_ie_u32 t;\n  t.min_incl_x = wuffs_base__u32__max(r->min_incl_x, s.min_incl_x);\n  t.min_incl_y = wuffs_base__u32__max(r->min_incl_y, s.min_incl_y);\n  t.max_excl_x = wuffs_base__u32__min(r->max_excl_x, s.max_excl_x);\n  t.max_excl_y = wuffs_base__u32__min(r->max_excl_y, s.max_excl_y);\n  return t;\n}\n\nstatic inline wuffs_base__rect_ie_u32  //\nwuffs_base__rect_ie_u32__unite(const 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  wuffs_base__rect_ie_u32 t;\n  t.min_incl_x = wuffs_base__u32__min(r" +
+	"->min_incl_x, s.min_incl_x);\n  t.min_incl_y = wuffs_base__u32__min(r->min_incl_y, s.min_incl_y);\n  t.max_excl_x = wuffs_base__u32__max(r->max_excl_x, s.max_excl_x);\n  t.max_excl_y = wuffs_base__u32__max(r->max_excl_y, s.max_excl_y);\n  return t;\n}\n\nstatic inline bool  //\nwuffs_base__rect_ie_u32__contains(const wuffs_base__rect_ie_u32* r,\n                                  uint32_t x,\n                                  uint32_t y) {\n  return (r->min_incl_x <= x) && (x < r->max_excl_x) && (r->min_incl_y <= y) &&\n         (y < r->max_excl_y);\n}\n\nstatic inline bool  //\nwuffs_base__rect_ie_u32__contains_rect(const wuffs_base__rect_ie_u32* r,\n                                       wuffs_base__rect_ie_u32 s) {\n  return wuffs_base__rect_ie_u32__equals(\n      &s, wuffs_base__rect_ie_u32__intersect(r, s));\n}\n\nstatic inline uint32_t  //\nwuffs_base__rect_ie_u32__width(const 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  //\nwuffs_base__rect_ie_u32__he" +
+	"ight(const wuffs_base__rect_ie_u32* r) {\n  return wuffs_base__u32__sat_sub(r->max_excl_y, r->min_incl_y);\n}\n\n#ifdef __cplusplus\n\ninline bool  //\nwuffs_base__rect_ie_u32::is_empty() const {\n  return wuffs_base__rect_ie_u32__is_empty(this);\n}\n\ninline bool  //\nwuffs_base__rect_ie_u32::equals(wuffs_base__rect_ie_u32 s) const {\n  return wuffs_base__rect_ie_u32__equals(this, s);\n}\n\ninline wuffs_base__rect_ie_u32  //\nwuffs_base__rect_ie_u32::intersect(wuffs_base__rect_ie_u32 s) const {\n  return wuffs_base__rect_ie_u32__intersect(this, s);\n}\n\ninline wuffs_base__rect_ie_u32  //\nwuffs_base__rect_ie_u32::unite(wuffs_base__rect_ie_u32 s) const {\n  return wuffs_base__rect_ie_u32__unite(this, s);\n}\n\ninline bool  //\nwuffs_base__rect_ie_u32::contains(uint32_t x, uint32_t y) const {\n  return wuffs_base__rect_ie_u32__contains(this, x, y);\n}\n\ninline bool  //\nwuffs_base__rect_ie_u32::contains_rect(wuffs_base__rect_ie_u32 s) const {\n  return wuffs_base__rect_ie_u32__contains_rect(this, s);\n}\n\ninline uint32_t  //\nwuffs_base__rect_" +
+	"ie_u32::width() const {\n  return wuffs_base__rect_ie_u32__width(this);\n}\n\ninline uint32_t  //\nwuffs_base__rect_ie_u32::height() const {\n  return wuffs_base__rect_ie_u32__height(this);\n}\n\n#endif  // __cplusplus\n" +
 	""
 
 const baseCopyright = "" +
diff --git a/internal/cgen/expr.go b/internal/cgen/expr.go
index 3315531..4a326c8 100644
--- a/internal/cgen/expr.go
+++ b/internal/cgen/expr.go
@@ -461,8 +461,8 @@
 	t.IDU32:      "uint32_t",
 	t.IDU64:      "uint64_t",
 	t.IDBool:     "bool",
-	t.IDIOReader: "wuffs_base__io_reader",
-	t.IDIOWriter: "wuffs_base__io_writer",
+	t.IDIOReader: "wuffs_base__io_buffer*",
+	t.IDIOWriter: "wuffs_base__io_buffer*",
 }
 
 const noSuchCOperator = " no_such_C_operator "
diff --git a/internal/cgen/func.go b/internal/cgen/func.go
index 1030f78..3f8daf5 100644
--- a/internal/cgen/func.go
+++ b/internal/cgen/func.go
@@ -368,10 +368,12 @@
 
 		b.writes("goto suspend;suspend:") // The goto avoids the "unused label" warning.
 
-		b.printf("self->private_impl.%s%s[0] = coro_susp_point;\n",
+		b.printf("self->private_impl.%s%s[0] = "+
+			"wuffs_base__status__is_suspension(status) ? coro_susp_point : 0;\n",
 			pPrefix, g.currFunk.astFunc.FuncName().Str(g.tm))
 		if g.currFunk.astFunc.Public() {
-			b.printf("self->private_impl.active_coroutine = %d;\n", g.currFunk.coroID)
+			b.printf("self->private_impl.active_coroutine = "+
+				"wuffs_base__status__is_suspension(status) ? %d : 0;\n", g.currFunk.coroID)
 		}
 		if err := g.writeResumeSuspend(b, &g.currFunk, true); err != nil {
 			return err
@@ -418,13 +420,10 @@
 	for _, o := range n.In().Fields() {
 		o := o.AsField()
 		oTyp := o.XType()
-		if oTyp.Decorator() != t.IDPtr && !oTyp.IsRefined() {
-			// TODO: Also check elements, for array-typed arguments.
-			continue
-		}
 
+		// TODO: Also check elements, for array-typed arguments.
 		switch {
-		case oTyp.Decorator() == t.IDPtr:
+		case oTyp.IsIOType() || (oTyp.Decorator() == t.IDPtr):
 			checks = append(checks, fmt.Sprintf("!%s%s", aPrefix, o.Name().Str(g.tm)))
 
 		case oTyp.IsRefined():
diff --git a/internal/cgen/resume.go b/internal/cgen/resume.go
index 09bf05c..1ade456 100644
--- a/internal/cgen/resume.go
+++ b/internal/cgen/resume.go
@@ -236,8 +236,14 @@
 }
 
 func (h *resumabilityHelper) doAssign(r resumabilities, n *a.Assign, depth uint32) error {
-	if err := h.doExpr(r, n.RHS()); err != nil {
-		return err
+	if n.Operator() == t.IDEqQuestion {
+		if err := h.doExpr1(r, n.RHS(), subExprFilterNone, 0); err != nil {
+			return err
+		}
+	} else {
+		if err := h.doExpr(r, n.RHS()); err != nil {
+			return err
+		}
 	}
 
 	if n.LHS() == nil {
@@ -302,12 +308,14 @@
 				}
 				return nil
 			}
+			panic("TODO: unreachable; delete")
 			processOnlySubExprs = true
 
 		case subExprFilterAfterCoroutine:
 			if n.Effect().Coroutine() {
 				return nil
 			}
+			panic("TODO: unreachable; delete")
 		}
 	}
 
diff --git a/internal/cgen/statement.go b/internal/cgen/statement.go
index 6fca3e8..7a57e9d 100644
--- a/internal/cgen/statement.go
+++ b/internal/cgen/statement.go
@@ -267,8 +267,8 @@
 			cTyp = "writer"
 		}
 		name := e.Ident().Str(g.tm)
-		b.printf("wuffs_base__io_%s %s%d_%s%s = %s%s;\n",
-			cTyp, oPrefix, ioBindNum, prefix, name, prefix, name)
+		b.printf("wuffs_base__io_buffer* %s%d_%s%s = %s%s;\n",
+			oPrefix, ioBindNum, prefix, name, prefix, name)
 
 		// TODO: save / restore all iop vars, not just for local IO vars? How
 		// does this work if the io_bind body advances these pointers, either
@@ -277,25 +277,24 @@
 			b.printf("uint8_t *%s%d_%s%s%s = %s%s%s;\n",
 				oPrefix, ioBindNum, iopPrefix, prefix, name, iopPrefix, prefix, name)
 			b.printf("uint8_t *%s%d_%s%s%s = %s%s%s;\n",
+				oPrefix, ioBindNum, io0Prefix, prefix, name, io0Prefix, prefix, name)
+			b.printf("uint8_t *%s%d_%s%s%s = %s%s%s;\n",
 				oPrefix, ioBindNum, io1Prefix, prefix, name, io1Prefix, prefix, name)
+			b.printf("uint8_t *%s%d_%s%s%s = %s%s%s;\n",
+				oPrefix, ioBindNum, io2Prefix, prefix, name, io2Prefix, prefix, name)
 		}
 
 		if n.Keyword() == t.IDIOBind {
-			b.printf("wuffs_base__io_%s__set(&%s%s, &u_%s, &iop_%s%s, &io1_%s%s,",
-				cTyp, prefix, name, name, prefix, name, prefix, name)
+			b.printf("%s%s = wuffs_base__io_%s__set(&%s%s, &%s%s%s, &%s%s%s, &%s%s%s, &%s%s%s,",
+				prefix, name, cTyp, uPrefix, name, iopPrefix, prefix, name,
+				io0Prefix, prefix, name, io1Prefix, prefix, name, io2Prefix, prefix, name)
 			if err := g.writeExpr(b, n.Arg1(), 0); err != nil {
 				return err
 			}
 			b.writes(");\n")
 
 		} else {
-			// TODO: restrict (in the type checker or parser) that e is
-			// args.foo?
-			b.printf("wuffs_base__io_%s__set_limit(&%s%s, iop_%s%s,\n", cTyp, prefix, name, prefix, name)
-			if err := g.writeExpr(b, n.Arg1(), 0); err != nil {
-				return err
-			}
-			b.writes(");\n")
+			return fmt.Errorf("TODO: implement io_limit (or remove it from the parser)")
 		}
 	}
 
@@ -318,7 +317,11 @@
 			b.printf("%s%s%s = %s%d_%s%s%s;\n",
 				iopPrefix, prefix, name, oPrefix, ioBindNum, iopPrefix, prefix, name)
 			b.printf("%s%s%s = %s%d_%s%s%s;\n",
+				io0Prefix, prefix, name, oPrefix, ioBindNum, io0Prefix, prefix, name)
+			b.printf("%s%s%s = %s%d_%s%s%s;\n",
 				io1Prefix, prefix, name, oPrefix, ioBindNum, io1Prefix, prefix, name)
+			b.printf("%s%s%s = %s%d_%s%s%s;\n",
+				io2Prefix, prefix, name, oPrefix, ioBindNum, io2Prefix, prefix, name)
 		}
 	}
 	b.writes("}\n")
diff --git a/internal/cgen/var.go b/internal/cgen/var.go
index 5a2cb50..9263d1c 100644
--- a/internal/cgen/var.go
+++ b/internal/cgen/var.go
@@ -95,37 +95,33 @@
 	}
 
 	preName := prefix + name.Str(g.tm)
-	i0, i1 := "meta.ri", "meta.wi"
+	i1, i2 := "meta.ri", "meta.wi"
 	if typ.QID()[1] == t.IDIOWriter {
-		i0, i1 = "meta.wi", "data.len"
+		i1, i2 = "meta.wi", "data.len"
 	}
 
 	if header {
 		b.printf("uint8_t* %s%s = NULL;", iopPrefix, preName)
 		b.printf("uint8_t* %s%s WUFFS_BASE__POTENTIALLY_UNUSED = NULL;", io0Prefix, preName)
 		b.printf("uint8_t* %s%s WUFFS_BASE__POTENTIALLY_UNUSED = NULL;", io1Prefix, preName)
+		b.printf("uint8_t* %s%s WUFFS_BASE__POTENTIALLY_UNUSED = NULL;", io2Prefix, preName)
 	}
 
-	b.printf("if (%s.private_impl.buf) {", preName)
-
-	b.printf("%s%s = %s.private_impl.buf->data.ptr + %s.private_impl.buf->%s;",
-		iopPrefix, preName, preName, preName, i0)
+	b.printf("if (%s) {", preName)
 
 	if header {
-		b.printf("if (!%s.private_impl.mark) {", preName)
-		b.printf("%s.private_impl.mark = %s%s;", preName, iopPrefix, preName)
-		b.printf("%s.private_impl.limit = %s.private_impl.buf->data.ptr + %s.private_impl.buf->%s;",
-			preName, preName, preName, i1)
-		b.printf("}\n")
+		b.printf("%s%s = %s->data.ptr;", io0Prefix, preName, preName)
+		b.printf("%s%s = %s%s + %s->%s;", io1Prefix, preName, io0Prefix, preName, preName, i1)
+		b.printf("%s%s = %s%s;", iopPrefix, preName, io1Prefix, preName)
+		b.printf("%s%s = %s%s + %s->%s;", io2Prefix, preName, io0Prefix, preName, preName, i2)
 
 		if typ.QID()[1] == t.IDIOWriter {
-			b.printf("if (%s.private_impl.buf->meta.closed) {", preName)
-			b.printf("%s.private_impl.limit = %s%s;", preName, iopPrefix, preName)
+			b.printf("if (%s->meta.closed) {", preName)
+			b.printf("%s%s = %s%s;", io2Prefix, preName, iopPrefix, preName)
 			b.printf("}\n")
 		}
-
-		b.printf("%s%s = %s.private_impl.mark;", io0Prefix, preName, preName)
-		b.printf("%s%s = %s.private_impl.limit;", io1Prefix, preName, preName)
+	} else {
+		b.printf("%s%s = %s->data.ptr + %s->%s;", iopPrefix, preName, preName, preName, i1)
 	}
 
 	b.printf("}\n")
@@ -158,15 +154,13 @@
 	}
 
 	preName := prefix + name.Str(g.tm)
-	i0 := "ri"
+	index := "ri"
 	if typ.QID()[1] == t.IDIOWriter {
-		i0 = "wi"
+		index = "wi"
 	}
 
-	b.printf("if (%s.private_impl.buf) {", preName)
-	b.printf("%s.private_impl.buf->meta.%s = ((size_t)(%s%s - %s.private_impl.buf->data.ptr));",
-		preName, i0, iopPrefix, preName, preName)
-	b.printf("}\n")
+	b.printf("if (%s) { %s->meta.%s = ((size_t)(%s%s - %s->data.ptr)); }\n",
+		preName, preName, index, iopPrefix, preName, preName)
 	return nil
 }
 
@@ -278,6 +272,11 @@
 		}
 
 		name := n.Name().Str(g.tm)
+
+		if typ.IsIOType() {
+			b.printf("wuffs_base__io_buffer %s%s = wuffs_base__null_io_buffer();\n", uPrefix, name)
+		}
+
 		if err := g.writeCTypeName(b, typ, vPrefix, name); err != nil {
 			return err
 		}
@@ -290,22 +289,16 @@
 		} else if typ.IsStatus() {
 			b.writes(" = NULL;\n")
 		} else if typ.IsIOType() {
-			typName := "reader"
-			if typ.QID()[1] == t.IDIOWriter {
-				typName = "writer"
-			}
-			b.printf(" = wuffs_base__null_io_%s();\n", typName)
+			b.printf(" = &%s%s;\n", uPrefix, name)
 		} else {
 			b.writes(" = {0};\n")
 		}
 
 		if typ.IsIOType() {
-			b.printf("wuffs_base__io_buffer %s%s WUFFS_BASE__POTENTIALLY_UNUSED = "+
-				"wuffs_base__null_io_buffer();\n", uPrefix, name)
-			preName := vPrefix + name
-			// TODO: io0_etc variables?
-			b.printf("uint8_t* %s%s WUFFS_BASE__POTENTIALLY_UNUSED = NULL;\n", iopPrefix, preName)
-			b.printf("uint8_t* %s%s WUFFS_BASE__POTENTIALLY_UNUSED = NULL;\n", io1Prefix, preName)
+			b.printf("uint8_t* %s%s%s WUFFS_BASE__POTENTIALLY_UNUSED = NULL;\n", iopPrefix, vPrefix, name)
+			b.printf("uint8_t* %s%s%s WUFFS_BASE__POTENTIALLY_UNUSED = NULL;\n", io0Prefix, vPrefix, name)
+			b.printf("uint8_t* %s%s%s WUFFS_BASE__POTENTIALLY_UNUSED = NULL;\n", io1Prefix, vPrefix, name)
+			b.printf("uint8_t* %s%s%s WUFFS_BASE__POTENTIALLY_UNUSED = NULL;\n", io2Prefix, vPrefix, name)
 		}
 	}
 	return nil
diff --git a/internal/testcut/testcut.go b/internal/testcut/testcut.go
index ba2d0ca..f15b8a5 100644
--- a/internal/testcut/testcut.go
+++ b/internal/testcut/testcut.go
@@ -43,7 +43,7 @@
 
 func Test(t *testing.T,
 	smallestValidMaxEncodedLen int,
-	cut func([]byte, int) (int, int, error),
+	cut func(io.Writer, []byte, int) (int, int, error),
 	newReader func(io.Reader) (io.ReadCloser, error),
 	filenames []string) {
 
@@ -90,8 +90,9 @@
 			if maxEncodedLen < smallestValidMaxEncodedLen {
 				continue
 			}
+			w0 := &bytes.Buffer{}
 			encoded := clone(full)
-			encLen, decLen, err := cut(encoded, maxEncodedLen)
+			encLen, decLen, err := cut(w0, encoded, maxEncodedLen)
 			if err != nil {
 				t.Errorf("f=%q, mEL=%d: cut: %v", filename, maxEncodedLen, err)
 				continue
@@ -107,13 +108,13 @@
 				continue
 			}
 
-			w := &bytes.Buffer{}
+			w1 := &bytes.Buffer{}
 			r, err := newReader(bytes.NewReader(encoded[:encLen]))
 			if err != nil {
 				t.Errorf("f=%q, mEL=%d: newReader: %v", filename, maxEncodedLen, err)
 				continue
 			}
-			if n, err := io.Copy(w, r); err != nil {
+			if n, err := io.Copy(w1, r); err != nil {
 				t.Errorf("f=%q, mEL=%d: io.Copy: %v", filename, maxEncodedLen, err)
 				continue
 			} else if n != int64(decLen) {
@@ -122,6 +123,11 @@
 				continue
 			}
 
+			if !bytes.Equal(w0.Bytes(), w1.Bytes()) {
+				t.Errorf("f=%q, mEL=%d: decoded bytes were not equal", filename, maxEncodedLen)
+				continue
+			}
+
 			if (maxEncodedLen == len(encoded)) && (int64(decLen) != fullDecodedLen) {
 				t.Errorf("f=%q, mEL=%d: full decode: got %d, want %d",
 					filename, maxEncodedLen, decLen, fullDecodedLen)
@@ -138,7 +144,7 @@
 
 func Benchmark(b *testing.B,
 	smallestValidMaxEncodedLen int,
-	cut func([]byte, int) (int, int, error),
+	cut func(io.Writer, []byte, int) (int, int, error),
 	newReader func(io.Reader) (io.ReadCloser, error),
 	filename string,
 	trimPrefix int,
@@ -168,7 +174,7 @@
 				continue
 			}
 			encoded := clone(full)
-			if _, _, err := cut(encoded, maxEncodedLen); err != nil {
+			if _, _, err := cut(nil, encoded, maxEncodedLen); err != nil {
 				b.Fatalf("cut: %v", err)
 			}
 			if maxEncodedLen >= len(full) {
diff --git a/lang/builtin/builtin.go b/lang/builtin/builtin.go
index 47de929..919fe86 100644
--- a/lang/builtin/builtin.go
+++ b/lang/builtin/builtin.go
@@ -19,15 +19,22 @@
 	t "github.com/google/wuffs/lang/token"
 )
 
+var FourCCs = [...][2]string{
+	{"ICCP", "International Color Consortium Profile"},
+	{"XMP ", "Extensible Metadata Platform"},
+}
+
 var Statuses = [...]string{
 	// Warnings.
 	`"@end of data"`,
+	`"@metadata reported"`,
 
 	// Suspensions.
 	`"$short read"`,
 	`"$short write"`,
 
 	// Errors.
+	`"#bad I/O position"`,
 	`"#bad argument (length too short)"`,
 	`"#bad argument"`,
 	`"#bad call sequence"`,
@@ -42,6 +49,7 @@
 	`"#initialize not called"`,
 	`"#interleaved coroutine calls"`,
 	`"#not enough data"`,
+	`"#unsupported option"`,
 	`"#too much data"`,
 }
 
@@ -209,11 +217,10 @@
 	"io_reader.peek_u64le() u64",
 
 	"io_reader.available() u64",
+	"io_reader.count_since(mark u64) u64",
+	"io_reader.mark() u64",
 	"io_reader.position() u64",
-	"io_reader.set!(s slice u8, closed bool)", // TODO: remove, as it's no longer used?
-	"io_reader.set_limit!(l u64)",             // TODO: remove, as it's no longer used?
-	"io_reader.set_mark!()",
-	"io_reader.since_mark() slice u8",
+	"io_reader.since(mark u64) slice u8",
 	"io_reader.take!(n u64) slice u8",
 
 	"io_reader.skip?(n u32)",
@@ -264,11 +271,11 @@
 	"io_writer.write_fast_u64le!(x u64)",
 
 	"io_writer.available() u64",
+	"io_writer.count_since(mark u64) u64",
+	"io_writer.history_available() u64",
+	"io_writer.mark() u64",
 	"io_writer.position() u64",
-	"io_writer.set!(s slice u8)",  // TODO: remove, as it's no longer used?
-	"io_writer.set_limit!(l u64)", // TODO: remove, as it's no longer used?
-	"io_writer.set_mark!()",
-	"io_writer.since_mark() slice u8",
+	"io_writer.since(mark u64) slice u8",
 
 	"io_writer.copy_from_slice!(s slice u8) u64",
 	"io_writer.copy_n_from_history!(n u32, distance u32) u32",
@@ -300,7 +307,7 @@
 	"frame_config.io_position() u64",
 
 	"frame_config.update!(bounds rect_ie_u32, duration u64[..0x7FFFFFFFFFFFFFFF], " +
-		"index u64, io_position u64, blend u8, disposal u8)",
+		"index u64, io_position u64, blend u8, disposal u8, background_color u32)",
 
 	// ---- image_config
 
@@ -315,8 +322,8 @@
 
 	// ---- pixel_swizzler
 
-	"pixel_swizzler.prepare!(dst_pixfmt u32, dst_palette slice u8, src_pixfmt u32, src_palette slice u8)",
-	"pixel_swizzler.swizzle_packed!(dst slice u8, dst_palette slice u8, src slice u8) u64",
+	"pixel_swizzler.prepare!(dst_pixfmt u32, dst_palette slice u8, src_pixfmt u32, src_palette slice u8) status",
+	"pixel_swizzler.swizzle_interleaved!(dst slice u8, dst_palette slice u8, src slice u8) u64",
 }
 
 // The "T1" and "T2" types here are placeholders for generic "slice T" or
diff --git a/lang/check/bounds.go b/lang/check/bounds.go
index a2d36ef..6e3c7d9 100644
--- a/lang/check/bounds.go
+++ b/lang/check/bounds.go
@@ -1113,10 +1113,10 @@
 }
 
 func (q *checker) canCopyNFromHistoryFast(recv *a.Expr, args []*a.Node) error {
-	// As per cgen's base-private.h, there are three pre-conditions:
+	// As per cgen's io-private.h, there are three pre-conditions:
 	//  - n <= this.available()
 	//  - distance > 0
-	//  - distance <= this.since_mark().length()
+	//  - distance <= this.history_available()
 
 	if len(args) != 2 {
 		return fmt.Errorf("check: internal error: inconsistent copy_n_from_history_fast arguments")
@@ -1174,7 +1174,7 @@
 		return fmt.Errorf("check: could not prove distance > 0")
 	}
 
-	// Check "distance <= this.since_mark().length()".
+	// Check "distance <= this.history_available()".
 check2:
 	for {
 		for _, x := range q.facts {
@@ -1192,22 +1192,18 @@
 				continue
 			}
 
-			// Check that the RHS is "recv.since_mark().length()".
+			// Check that the RHS is "recv.history_available()".
 			y, method, yArgs := splitReceiverMethodArgs(x.RHS().AsExpr())
-			if method != t.IDLength || len(yArgs) != 0 {
+			if method != t.IDHistoryAvailable || len(yArgs) != 0 {
 				continue
 			}
-			z, method, zArgs := splitReceiverMethodArgs(y)
-			if method != t.IDSinceMark || len(zArgs) != 0 {
-				continue
-			}
-			if !z.Eq(recv) {
+			if !y.Eq(recv) {
 				continue
 			}
 
 			break check2
 		}
-		return fmt.Errorf("check: could not prove distance <= %s.since_mark().length()", recv.Str(q.tm))
+		return fmt.Errorf("check: could not prove distance <= %s.history_available()", recv.Str(q.tm))
 	}
 
 	return nil
diff --git a/lang/token/list.go b/lang/token/list.go
index 85cb5c5..765ef80 100644
--- a/lang/token/list.go
+++ b/lang/token/list.go
@@ -459,15 +459,15 @@
 
 	IDDecodeFrameOptions = ID(0x158)
 
-	IDCanUndoByte     = ID(0x160)
-	IDPosition        = ID(0x161)
-	IDSetLimit        = ID(0x162)
-	IDSetMark         = ID(0x163)
-	IDSinceMark       = ID(0x164)
-	IDSinceMarkLength = ID(0x165)
-	IDSkip            = ID(0x166)
-	IDSkipFast        = ID(0x167)
-	IDTake            = ID(0x168)
+	IDCanUndoByte      = ID(0x160)
+	IDCountSince       = ID(0x161)
+	IDHistoryAvailable = ID(0x162)
+	IDMark             = ID(0x163)
+	IDPosition         = ID(0x164)
+	IDSince            = ID(0x165)
+	IDSkip             = ID(0x166)
+	IDSkipFast         = ID(0x167)
+	IDTake             = ID(0x168)
 
 	IDCopyFromSlice        = ID(0x170)
 	IDCopyNFromHistory     = ID(0x171)
@@ -791,15 +791,15 @@
 
 	IDDecodeFrameOptions: "decode_frame_options",
 
-	IDCanUndoByte:     "can_undo_byte",
-	IDPosition:        "position",
-	IDSetLimit:        "set_limit",
-	IDSetMark:         "set_mark",
-	IDSinceMark:       "since_mark",
-	IDSinceMarkLength: "since_mark_length",
-	IDSkip:            "skip",
-	IDSkipFast:        "skip_fast",
-	IDTake:            "take",
+	IDCanUndoByte:      "can_undo_byte",
+	IDCountSince:       "count_since",
+	IDHistoryAvailable: "history_available",
+	IDMark:             "mark",
+	IDPosition:         "position",
+	IDSince:            "since",
+	IDSkip:             "skip",
+	IDSkipFast:         "skip_fast",
+	IDTake:             "take",
 
 	IDCopyFromSlice:        "copy_from_slice",
 	IDCopyNFromHistory:     "copy_n_from_history",
diff --git a/lib/flatecut/flatecut.go b/lib/flatecut/flatecut.go
index cc4e7fc..0557168 100644
--- a/lib/flatecut/flatecut.go
+++ b/lib/flatecut/flatecut.go
@@ -23,14 +23,19 @@
 package flatecut
 
 import (
+	"bytes"
+	"compress/flate"
 	"errors"
+	"io"
 )
 
 var (
 	errMaxEncodedLenTooSmall = errors.New("flatecut: maxEncodedLen is too small")
 
-	errInternalNoProgress   = errors.New("flatecut: internal: no progress")
-	errInternalSomeProgress = errors.New("flatecut: internal: some progress")
+	errInternalInconsistentDecodedLen = errors.New("flatecut: internal: inconsistent decodedLen")
+	errInternalNoProgress             = errors.New("flatecut: internal: no progress")
+	errInternalReplaceWithSingleBlock = errors.New("flatecut: internal: replace with single block")
+	errInternalSomeProgress           = errors.New("flatecut: internal: some progress")
 
 	errInvalidBadBlockLength = errors.New("flatecut: invalid input: bad block length")
 	errInvalidBadBlockType   = errors.New("flatecut: invalid input: bad block type")
@@ -84,6 +89,13 @@
 	mostNegativeInt32 = -0x80000000
 )
 
+func loadU64LE(b []byte) uint64 {
+	_ = b[7] // bounds check hint to compiler; see golang.org/issue/14808
+	return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 |
+		uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56
+
+}
+
 type bitstream struct {
 	// bytes[index] is the next byte to load into the 'bits' field.
 	bytes []byte
@@ -91,7 +103,7 @@
 
 	// The low nBits bits of the 'bits' field hold the next bits (in LSB-first
 	// order).
-	bits  uint32
+	bits  uint64
 	nBits uint32
 }
 
@@ -100,13 +112,13 @@
 		if b.index >= len(b.bytes) {
 			return mostNegativeInt32
 		}
-		b.bits |= uint32(b.bytes[b.index]) << b.nBits
+		b.bits |= uint64(b.bytes[b.index]) << b.nBits
 		b.nBits += 8
 		b.index++
 	}
 
 	mask := ((uint32(1)) << nBits) - 1
-	ret := b.bits & mask
+	ret := uint32(b.bits) & mask
 	b.bits >>= nBits
 	b.nBits -= nBits
 	return int32(ret)
@@ -125,12 +137,15 @@
 // which results in:
 //
 // huffman{
-//   counts: []uint32{
+//   counts: [maxCodeBits + 1]uint32{
 //     2: 1, 3: 5, 4: 2,
 //   },
-//   symbols: []int32{
+//   symbols: [maxNumCodes]int32{
 //     0: 'F', 1: 'A', 2: 'B', 3: 'C', 4: 'D', 5: 'E', 6: 'G', 7: 'H',
 //   },
+//   lookUpTable: [256]uint32{
+//     etc,
+//   },
 // }
 //
 // Continuing the example from the RFC, decoding "1110" from the bitstream will
@@ -138,9 +153,49 @@
 type huffman struct {
 	counts  [maxCodeBits + 1]uint32
 	symbols [maxNumCodes]int32
+
+	// lookUpTable holds cached results of the slowDecode method.
+	//
+	// The uint8 key is the next 8 bits of input.
+	//
+	// The uint32 value's low 16 bits are the symbol, the high 16 bits are the
+	// number of bits used to encode that symbol.
+	//
+	// Zero means that the entry is invalid. For example, the encoded symbol
+	// might be longer than 8 bits.
+	lookUpTable [256]uint32
 }
 
 func (h *huffman) decode(b *bitstream) int32 {
+	if b.nBits >= 8 {
+		// No-op.
+	} else if b.index < (len(b.bytes) - 8) {
+		// This is "Variant 4" of
+		// https://fgiesen.wordpress.com/2018/02/20/reading-bits-in-far-too-many-ways-part-2/
+		u := loadU64LE(b.bytes[b.index:])
+		b.bits |= u << b.nBits
+		b.index += int((63 - b.nBits) >> 3)
+		b.nBits |= 56
+	} else if b.index < len(b.bytes) {
+		b.bits |= uint64(b.bytes[b.index]) << b.nBits
+		b.nBits += 8
+		b.index++
+	} else {
+		goto slow
+	}
+
+	if x := h.lookUpTable[b.bits&0xFF]; x != 0 {
+		n := x >> 16
+		b.bits >>= n
+		b.nBits -= n
+		return int32(x & 0xFFFF)
+	}
+
+slow:
+	return h.slowDecode(b)
+}
+
+func (h *huffman) slowDecode(b *bitstream) int32 {
 	code := uint32(0)     // The i bits from the bitstream.
 	first := uint32(0)    // The first Huffman code of length i.
 	symIndex := uint32(0) // How many elements of h.symbols we've gone past.
@@ -154,12 +209,12 @@
 			if b.index >= len(b.bytes) {
 				return mostNegativeInt32
 			}
-			b.bits = uint32(b.bytes[b.index])
+			b.bits = uint64(b.bytes[b.index])
 			b.nBits = 8
 			b.index++
 		}
 
-		code |= b.bits & 1
+		code |= uint32(b.bits & 1)
 		b.bits >>= 1
 		b.nBits -= 1
 
@@ -199,7 +254,7 @@
 	for _, x := range lengths {
 		h.counts[x]++
 	}
-	if h.counts[0] == uint32(len(lengths)) {
+	if h.counts[0] >= uint32(len(lengths)) {
 		return 0, 0, errInvalidBadHuffmanTree
 	}
 
@@ -230,7 +285,12 @@
 		}
 	}
 	if remaining != 0 {
-		return 0, 0, errInvalidBadHuffmanTree
+		if ((h.counts[0] + 1) == uint32(len(lengths))) && (h.counts[1] == 1) {
+			// No-op. We allow a degenerate Huffman tree with only one code (of
+			// length 1 bit).
+		} else {
+			return 0, 0, errInvalidBadHuffmanTree
+		}
 	}
 
 	offsets := [maxCodeBits + 1]uint32{}
@@ -244,18 +304,68 @@
 			offsets[length]++
 		}
 	}
+	h.constructLookUpTable()
 	return endCodeBits, endCodeNBits, nil
 }
 
-// cutEmpty sets encoding[:2] to contain valid DEFLATE-encoded data. Decoding
-// that data produces zero bytes.
-func cutEmpty(encoded []byte, maxEncodedLen int) (encodedLen int, decodedLen int, retErr error) {
+func (h *huffman) constructLookUpTable() {
+	b := bitstream{
+		bytes: []byte{0x00},
+	}
+	for i := range h.lookUpTable {
+		b.bytes[0] = uint8(i)
+		b.index = 0
+		b.bits = 0
+		b.nBits = 0
+		if x := h.slowDecode(&b); x >= 0 {
+			h.lookUpTable[i] = ((8 - b.nBits) << 16) | uint32(x)
+		} else {
+			h.lookUpTable[i] = 0
+		}
+	}
+}
+
+// cutSingleBlock is an implementation of Cut whose output consists of a single
+// DEFLATE block.
+//
+// If maxEncodedLen is sufficiently large, this will be a Stored block (i.e. a
+// header followed by literal bytes). Otherwise, it will set encoding[:2] so
+// that decoding produces zero bytes.
+//
+// A precondition is that maxEncodedLen >= SmallestValidMaxEncodedLen.
+func cutSingleBlock(encoded []byte, maxEncodedLen int) (encodedLen int, decodedLen int, retErr error) {
 	if maxEncodedLen < SmallestValidMaxEncodedLen {
 		panic("unreachable")
 	}
+
+	// Try re-encoding as a single Stored block: up to 0xFFFF literal bytes,
+	// with a 5 byte prefix.
+	if maxEncodedLen > 5 {
+		n := maxEncodedLen - 5
+		if n > 0xFFFF {
+			n = 0xFFFF
+		}
+
+		buf := make([]byte, n)
+		n, err := io.ReadFull(flate.NewReader(bytes.NewReader(encoded)), buf)
+		if err != nil && err != io.EOF && err != io.ErrUnexpectedEOF {
+			return 0, 0, err
+		}
+
+		if n > 0 {
+			encoded[0] = 0x01 // finalBlock = true, blockType = 0 (Stored).
+			encoded[1] = uint8(n >> 0)
+			encoded[2] = uint8(n >> 8)
+			encoded[3] = ^encoded[1]
+			encoded[4] = ^encoded[2]
+			copy(encoded[5:], buf[:n])
+			return n + 5, n, nil
+		}
+	}
+
 	// Set encoded[:2] to hold:
 	//  - 1 bit   ...._...._...._...1  finalBlock   = true.
-	//  - 2 bits  ...._...._...._.01.  blockType    = 1 (static Huffman).
+	//  - 2 bits  ...._...._...._.01.  blockType    = 1 (Static Huffman).
 	//  - 7 bits  ...._..00_0000_0...  litLenSymbol = 256 (end-of-block).
 	//  - 6 bits  0000_00.._...._....  padding.
 	encoded[0] = 0x03
@@ -267,11 +377,16 @@
 // DEFLATE-compressed data, assuming that encoded starts off containing valid
 // DEFLATE-compressed data.
 //
+// If a nil error is returned, then encodedLen <= maxEncodedLen will hold.
+//
 // Decompressing that modified, shorter byte slice produces a prefix (of length
 // decodedLen) of the decompression of the original, longer byte slice.
 //
+// If w is non-nil, that prefix is also written to w. If a non-nil error is
+// returned, incomplete data might still be written to w.
+//
 // It does not necessarily return the largest possible decodedLen.
-func Cut(encoded []byte, maxEncodedLen int) (encodedLen int, decodedLen int, retErr error) {
+func Cut(w io.Writer, encoded []byte, maxEncodedLen int) (encodedLen int, decodedLen int, retErr error) {
 	if maxEncodedLen < SmallestValidMaxEncodedLen {
 		return 0, 0, errMaxEncodedLenTooSmall
 	}
@@ -292,7 +407,28 @@
 		},
 		maxEncodedLen: maxEncodedLen,
 	}
-	return c.cut()
+	encodedLen, decodedLen, err := c.cut()
+	if err != nil {
+		return 0, 0, err
+	}
+
+	if w != nil {
+		// TODO: writing to w directly, in cutter's doStored and doHuffman,
+		// might be faster than re-walking the bitstream with compress/flate.
+		r := flate.NewReader(bytes.NewReader(encoded[:encodedLen]))
+		if n, err := io.Copy(w, r); err != nil {
+			r.Close()
+			return 0, 0, err
+		} else if n != int64(decodedLen) {
+			r.Close()
+			return 0, 0, errInternalInconsistentDecodedLen
+		}
+		if err := r.Close(); err != nil {
+			return 0, 0, err
+		}
+	}
+
+	return encodedLen, decodedLen, nil
 }
 
 type cutter struct {
@@ -320,6 +456,10 @@
 
 		finalBlockIndex := c.bits.index
 		finalBlockNBits := c.bits.nBits
+		for finalBlockNBits >= 8 {
+			finalBlockIndex--
+			finalBlockNBits -= 8
+		}
 
 		blockType := c.bits.take(2)
 		if blockType < 0 {
@@ -331,13 +471,18 @@
 		case 0:
 			err = c.doStored()
 		case 1:
-			err = c.doStaticHuffman()
+			err = c.doStaticHuffman(prevFinalBlockIndex < 0)
 		case 2:
-			err = c.doDynamicHuffman()
+			err = c.doDynamicHuffman(prevFinalBlockIndex < 0)
 		case 3:
 			return 0, 0, errInvalidBadBlockType
 		}
 
+		for c.bits.nBits >= 8 {
+			c.bits.index--
+			c.bits.nBits -= 8
+		}
+
 		switch err {
 		case nil:
 			if finalBlock == 0 {
@@ -348,15 +493,15 @@
 
 		case errInternalNoProgress:
 			if prevFinalBlockIndex < 0 {
-				return cutEmpty(c.bits.bytes, c.maxEncodedLen)
+				return cutSingleBlock(c.bits.bytes, c.maxEncodedLen)
 			}
 
 			// Un-read to just before the finalBlock bit.
 			c.bits.index = finalBlockIndex
 			c.bits.nBits = finalBlockNBits + 1
-			if c.bits.nBits == 8 {
+			for c.bits.nBits >= 8 {
 				c.bits.index--
-				c.bits.nBits = 0
+				c.bits.nBits -= 8
 			}
 
 			finalBlockIndex = prevFinalBlockIndex
@@ -370,6 +515,9 @@
 			mask := uint32(1) << n
 			c.bits.bytes[finalBlockIndex-1] |= uint8(mask)
 
+		case errInternalReplaceWithSingleBlock:
+			return cutSingleBlock(c.bits.bytes, c.maxEncodedLen)
+
 		default:
 			return 0, 0, err
 
@@ -387,9 +535,14 @@
 }
 
 func (c *cutter) doStored() error {
-	if (c.maxEncodedLen - c.bits.index) < 4 {
+	for c.bits.nBits >= 8 {
+		c.bits.index--
+		c.bits.nBits -= 8
+	}
+	if (c.maxEncodedLen < c.bits.index) || ((c.maxEncodedLen - c.bits.index) < 4) {
 		return errInternalNoProgress
 	}
+
 	length := uint32(c.bits.bytes[c.bits.index+0]) | uint32(c.bits.bytes[c.bits.index+1])<<8
 	invLen := uint32(c.bits.bytes[c.bits.index+2]) | uint32(c.bits.bytes[c.bits.index+3])<<8
 	if length+invLen != 0xFFFF {
@@ -426,7 +579,7 @@
 	return errInternalSomeProgress
 }
 
-func (c *cutter) doStaticHuffman() error {
+func (c *cutter) doStaticHuffman(isFirstBlock bool) error {
 	const (
 		numLCodes = 288
 		numDCodes = 32
@@ -451,10 +604,10 @@
 		lengths[i] = 5
 	}
 
-	return c.doHuffman(lengths[:numLCodes], lengths[numLCodes:])
+	return c.doHuffman(isFirstBlock, lengths[:numLCodes], lengths[numLCodes:])
 }
 
-func (c *cutter) doDynamicHuffman() error {
+func (c *cutter) doDynamicHuffman(isFirstBlock bool) error {
 	numLCodes := 257 + c.bits.take(5)
 	if numLCodes < 0 {
 		return errInvalidNotEnoughData
@@ -527,10 +680,10 @@
 		}
 	}
 
-	return c.doHuffman(lengths[:numLCodes], lengths[numLCodes:])
+	return c.doHuffman(isFirstBlock, lengths[:numLCodes], lengths[numLCodes:])
 }
 
-func (c *cutter) doHuffman(lLengths []uint32, dLengths []uint32) error {
+func (c *cutter) doHuffman(isFirstBlock bool, lLengths []uint32, dLengths []uint32) error {
 	err := error(nil)
 	if c.endCodeBits, c.endCodeNBits, err = c.lHuff.construct(lLengths); err != nil {
 		return err
@@ -542,6 +695,10 @@
 		return err
 	}
 
+	for c.bits.nBits >= 8 {
+		c.bits.index--
+		c.bits.nBits -= 8
+	}
 	if c.bits.index > c.maxEncodedLen {
 		return errInternalNoProgress
 	}
@@ -609,8 +766,25 @@
 	if checkpointIndex < 0 {
 		return errInternalNoProgress
 	}
+
+	// If we're the first block in the DEFLATE stream, check if we would be
+	// better off replacing the Huffman block with a Stored block.
+	if isFirstBlock && (c.maxEncodedLen > 5) {
+		n := c.maxEncodedLen - 5
+		if n > 0xFFFF {
+			n = 0xFFFF
+		}
+		if c.decodedLen < int32(n) {
+			return errInternalReplaceWithSingleBlock
+		}
+	}
+
 	c.bits.index = checkpointIndex
 	c.bits.nBits = checkpointNBits
+	for c.bits.nBits >= 8 {
+		c.bits.index--
+		c.bits.nBits -= 8
+	}
 	c.writeEndCode()
 	return errInternalSomeProgress
 }
diff --git a/lib/flatecut/flatecut_test.go b/lib/flatecut/flatecut_test.go
index 7c649ca..5b0035e 100644
--- a/lib/flatecut/flatecut_test.go
+++ b/lib/flatecut/flatecut_test.go
@@ -15,8 +15,10 @@
 package flatecut
 
 import (
+	"bytes"
 	"compress/flate"
 	"io"
+	"io/ioutil"
 	"testing"
 
 	"github.com/google/wuffs/internal/testcut"
@@ -39,3 +41,191 @@
 func newReader(r io.Reader) (io.ReadCloser, error) {
 	return flate.NewReader(r), nil
 }
+
+func decodeFlate(src []byte) string {
+	dst, err := ioutil.ReadAll(flate.NewReader(bytes.NewReader(src)))
+	if err != nil {
+		return err.Error()
+	}
+	return string(dst)
+}
+
+func TestHuffmanDecode(t *testing.T) {
+	// This exercises the "ABCDEFGH" example from RFC 1951 section 3.2.2,
+	// discussed in the "type huffman" doc comment.
+
+	const src = "HEADFACE"
+	codes := []string{
+		'A': " 010",
+		'B': " 011",
+		'C': " 100",
+		'D': " 101",
+		'E': " 110",
+		'F': "  00",
+		'G': "1110",
+		'H': "1111",
+	}
+
+	encoded := []byte(nil)
+	encBits := uint32(0)
+	encNBits := uint32(0)
+	for _, s := range src {
+		for _, c := range codes[s] {
+			if c == ' ' {
+				continue
+			}
+
+			if c == '1' {
+				encBits |= 1 << encNBits
+			}
+			encNBits++
+
+			if encNBits == 8 {
+				encoded = append(encoded, uint8(encBits))
+				encBits = 0
+				encNBits = 0
+			}
+		}
+	}
+	if encNBits > 0 {
+		encoded = append(encoded, uint8(encBits))
+	}
+
+	h := huffman{
+		counts: [maxCodeBits + 1]uint32{
+			2: 1, 3: 5, 4: 2,
+		},
+		symbols: [maxNumCodes]int32{
+			0: 'F', 1: 'A', 2: 'B', 3: 'C', 4: 'D', 5: 'E', 6: 'G', 7: 'H',
+		},
+	}
+	h.constructLookUpTable()
+
+	b := &bitstream{
+		bytes: encoded,
+	}
+
+	decoded := []byte(nil)
+	for _ = range src {
+		c := h.decode(b)
+		if c < 0 {
+			decoded = append(decoded, '!')
+			break
+		}
+		decoded = append(decoded, uint8(c))
+	}
+
+	if got, want := string(decoded), src; got != want {
+		t.Fatalf("got %q, want %q", got, want)
+	}
+}
+
+// A DEFLATE encoding of the first 64 bytes of the AUTHORS file in the
+// repository root directory.
+//
+// The encoding uses a Dynamic Huffman block, but one whose H-D Huffman
+// distance tree is degenerate (there's a mapping for the "0" code but no
+// mapping for the "1" code) and unused.
+//
+// The first 25-30 bytes contain the encoded H-L and H-D Huffman trees. The
+// last 30-35 bytes contain the actual payload, encoded with H-L.
+//
+// The high 7 bits of the final byte is unused padding.
+const (
+	degenerateHuffmanDec = "# This is the official list of Wuffs authors for copyright purpo"
+	degenerateHuffmanEnc = "" +
+		"\x05\xc0\xc1\x09\xc5\x30\x0c\x03\xd0\x55\x04\x7f\xa5\x0f\x3d\x87" +
+		"\x50\xd5\x82\x80\x82\xed\x1c\xba\x7d\xdf\x0f\xff\x50\x41\x85\x8e" +
+		"\x1b\x26\x35\x35\x16\x96\xaa\x61\xe2\x3a\x64\x61\x9c\x0e\x67\x81" +
+		"\x4e\x4c\xef\x37\xf5\x44\x63\x9f\xdc\xfe\x00"
+)
+
+func TestReplaceHuffmanWithStored(t *testing.T) {
+	const dec = degenerateHuffmanDec
+	const enc = degenerateHuffmanEnc
+	if (len(dec) != 64) || (len(enc) != 59) {
+		panic("inconsistent const string lengths")
+	}
+	if got, want := decodeFlate([]byte(enc)), dec; got != want {
+		t.Fatalf("before Cut: got %q, want %q", got, want)
+	}
+
+	for i := 4; i <= 59; i += 5 {
+		b := []byte(enc)
+		encLen, decLen, err := Cut(nil, b, i)
+		if err != nil {
+			t.Errorf("i=%d: %v", i, err)
+			continue
+		}
+		if encLen < 1 {
+			t.Errorf("i=%d: encLen: got %d, want >= 1", i, encLen)
+			continue
+		} else if encLen > len(enc) {
+			t.Errorf("i=%d: encLen: got %d, want <= %d", i, encLen, len(enc))
+			continue
+		}
+		// If we can make some progress (decLen > 0), even if the input uses a
+		// Huffman block, one option is to re-encode to a single Stored block
+		// (for 5 bytes of overhead). It's single because len(dec) < 0xFFFF.
+		// Regardless of whether the cut form uses a Huffman or Stored block,
+		// we should be able to produce at least i-5 bytes of decoded output.
+		if (decLen > 0) && (i > 5) && (decLen < i-5) {
+			t.Errorf("i=%d: decLen: got %d, want >= %d", i, decLen, i-5)
+			continue
+		} else if decLen > len(dec) {
+			t.Errorf("i=%d: decLen: got %d, want <= %d", i, decLen, len(dec))
+			continue
+		}
+
+		if got, want := decodeFlate(b[:encLen]), dec[:decLen]; got != want {
+			t.Errorf("i=%d: after Cut: got %q, want %q", i, got, want)
+			continue
+		}
+
+		// Check that we are using a space-efficient block type.
+		{
+			got := (b[0] >> 1) & 3
+			want := uint8(0xFF)
+
+			switch i {
+			case 4:
+				want = 1 // Static Huffman, for a decLen of 0.
+			case 9:
+				want = 0 // Stored.
+			case 59:
+				want = 2 // Dynamic Huffman.
+			default:
+				continue
+			}
+
+			if got != want {
+				t.Errorf("i=%d: block type: got %d, want %d", i, got, want)
+			}
+		}
+	}
+}
+
+func TestDegenerateHuffmanUnused(t *testing.T) {
+	const dec = degenerateHuffmanDec
+	const enc = degenerateHuffmanEnc
+
+	// Cutting 1 byte off the end of the encoded form will lead to cutting n
+	// bytes off the decoded form. Coincidentally, n equals 1, even though each
+	// decoded byte (8 bits) is packed into smaller number of bits, as most of
+	// the final encoded byte's bits are unused padding.
+	const n = 1
+
+	b := []byte(enc)
+	encLen, decLen, err := Cut(nil, b, len(enc)-1)
+	if err != nil {
+		t.Fatalf("Cut: %v", err)
+	} else if encLen != len(enc)-1 {
+		t.Fatalf("encLen: got %d, want %d", encLen, len(enc)-1)
+	} else if decLen != len(dec)-n {
+		t.Fatalf("decLen: got %d, want %d", decLen, len(dec)-n)
+	}
+
+	if got, want := decodeFlate(b[:encLen]), dec[:decLen]; got != want {
+		t.Fatalf("after Cut: got %q, want %q", got, want)
+	}
+}
diff --git a/lib/rac/rac.go b/lib/rac/rac.go
index 905fb15..03b9ffb 100644
--- a/lib/rac/rac.go
+++ b/lib/rac/rac.go
@@ -26,7 +26,7 @@
 package rac
 
 const (
-	// MaxCSize is the maximum RAC file size (in both CSpace and DSpace).
+	// MaxSize is the maximum RAC file size (in both CSpace and DSpace).
 	MaxSize = (1 << 48) - 1
 
 	// invalidCOffsetCLength is ((1 << 64) - 1).
diff --git a/lib/rac/writer.go b/lib/rac/writer.go
index 002365f..b699120 100644
--- a/lib/rac/writer.go
+++ b/lib/rac/writer.go
@@ -21,6 +21,13 @@
 	"sort"
 )
 
+func btoi(b bool) int {
+	if b {
+		return 1
+	}
+	return 0
+}
+
 func isZeroOrAPowerOf2(x uint64) bool {
 	return (x & (x - 1)) == 0
 }
@@ -593,8 +600,18 @@
 	w.buffer[4] = uint8(checksum >> 0)
 	w.buffer[5] = uint8(checksum >> 8)
 
-	_, err := w.w.Write(w.buffer[:size])
-	return err
+	if _, err := w.w.Write(w.buffer[:size]); err != nil {
+		return err
+	}
+
+	for i, o := range n.children {
+		if len(o.children) != 0 {
+			if err := w.writeIndex(&n.children[i]); err != nil {
+				return err
+			}
+		}
+	}
+	return nil
 }
 
 func calcCLength(primarySize int) uint64 {
@@ -640,37 +657,51 @@
 	}
 
 	resources := map[OptResource]bool{}
-	arity := 0
 
 	for {
-		i, j, newNodes := 0, 0, []node(nil)
+		i, j, arity, newNodes := 0, 0, 0, []node(nil)
 		for ; j < len(nodes); j++ {
 			o := &nodes[j]
 
-			arity++
-			if (o.secondary != 0) && !resources[o.secondary] {
-				resources[o.secondary] = true
-				arity++
-			}
-			if (o.tertiary != 0) && !resources[o.tertiary] {
-				resources[o.tertiary] = true
-				arity++
-			}
+			new2 := (o.secondary != 0) && !resources[o.secondary]
+			new3 := (o.tertiary != 0) && !resources[o.tertiary]
+			arity += 1 + btoi(new2) + btoi(new3)
 			if arity <= 0xFF {
+				if new2 {
+					resources[o.secondary] = true
+				}
+				if new3 {
+					resources[o.tertiary] = true
+				}
 				continue
 			}
 
 			newNodes = append(newNodes, makeBranch(nodes[i:j], resources))
-			i = j
-			arity = 0
 			if len(resources) != 0 {
 				resources = map[OptResource]bool{}
 			}
+
+			i = j
+			arity = 1
+			if o.secondary != 0 {
+				resources[o.secondary] = true
+				arity++
+			}
+			if o.tertiary != 0 {
+				resources[o.tertiary] = true
+				arity++
+			}
 		}
 
 		if i == 0 {
 			return makeBranch(nodes, resources)
 		}
+
+		newNodes = append(newNodes, makeBranch(nodes[i:], resources))
+		if len(resources) != 0 {
+			resources = map[OptResource]bool{}
+		}
+
 		nodes, newNodes = newNodes, nil
 	}
 }
diff --git a/lib/rac/writer_test.go b/lib/rac/writer_test.go
index 08982af..596c407 100644
--- a/lib/rac/writer_test.go
+++ b/lib/rac/writer_test.go
@@ -25,6 +25,10 @@
 	"testing"
 )
 
+const bytesPerHexDumpLine = 79
+
+const fakeCodec = Codec(0xEE)
+
 // Note that these exact bytes depends on the zlib encoder's algorithm, but
 // there is more than one valid zlib encoding of any given input. This "compare
 // to golden output" test is admittedly brittle, as the standard library's zlib
@@ -164,7 +168,6 @@
 
 func testWriter(iloc IndexLocation, tempFile io.ReadWriter, cPageSize uint64, empty bool) error {
 	buf := &bytes.Buffer{}
-	const fakeCodec = Codec(0xEE)
 	w := &Writer{
 		Writer:        buf,
 		Codec:         fakeCodec,
@@ -211,3 +214,127 @@
 	}
 	return nil
 }
+
+func TestWriterMultiLevelIndex(t *testing.T) {
+	buf := &bytes.Buffer{}
+	w := &Writer{
+		Writer:        buf,
+		Codec:         fakeCodec,
+		IndexLocation: IndexLocationAtStart,
+		TempFile:      &bytes.Buffer{},
+	}
+
+	// Write 260 chunks with 3 resources. With the current "func gather"
+	// algorithm, this results in a root node with two children, both of which
+	// are branch nodes. The first branch contains 252 chunks and refers to 3
+	// resources (so that its arity is 255). The second branch contains 8
+	// chunks and refers to 1 resource (so that its arity is 9).
+	xRes := OptResource(0)
+	yRes := OptResource(0)
+	zRes := OptResource(0)
+	for i := 0; i < 260; i++ {
+		secondary := OptResource(0)
+		tertiary := OptResource(0)
+
+		switch i {
+		case 3:
+			xRes, _ = w.AddResource([]byte("XX"))
+			yRes, _ = w.AddResource([]byte("YY"))
+			secondary = xRes
+			tertiary = yRes
+
+		case 4:
+			zRes, _ = w.AddResource([]byte("ZZ"))
+			secondary = yRes
+			tertiary = zRes
+
+		case 259:
+			secondary = yRes
+		}
+
+		primary := []byte(fmt.Sprintf("p%02x", i&0xFF))
+		_ = w.AddChunk(0x10000, primary, secondary, tertiary)
+	}
+
+	if err := w.Close(); err != nil {
+		t.Fatalf("Close: %v", err)
+	}
+
+	encoded := buf.Bytes()
+	if got, want := len(encoded), 0x13E2; got != want {
+		t.Fatalf("len(encoded): got 0x%X, want 0x%X", got, want)
+	}
+
+	got := hex.Dump(encoded)
+	got = "" +
+		got[0x000*bytesPerHexDumpLine:0x008*bytesPerHexDumpLine] +
+		"...\n" +
+		got[0x080*bytesPerHexDumpLine:0x088*bytesPerHexDumpLine] +
+		"...\n" +
+		got[0x100*bytesPerHexDumpLine:0x110*bytesPerHexDumpLine] +
+		"...\n" +
+		got[0x13C*bytesPerHexDumpLine:]
+
+	const want = "" +
+		"00000000  72 c3 63 02 a0 24 00 ff  00 00 fc 00 00 00 00 ff  |r.c..$..........|\n" +
+		"00000010  00 00 04 01 00 00 00 ee  30 00 00 00 00 00 04 ff  |........0.......|\n" +
+		"00000020  30 10 00 00 00 00 01 ff  e2 13 00 00 00 00 01 02  |0...............|\n" +
+		"00000030  72 c3 63 ff 4c be 00 ff  00 00 00 00 00 00 00 ff  |r.c.L...........|\n" +
+		"00000040  00 00 00 00 00 00 00 ff  00 00 00 00 00 00 00 ff  |................|\n" +
+		"00000050  00 00 01 00 00 00 00 ff  00 00 02 00 00 00 00 ff  |................|\n" +
+		"00000060  00 00 03 00 00 00 00 01  00 00 04 00 00 00 00 02  |................|\n" +
+		"00000070  00 00 05 00 00 00 00 ff  00 00 06 00 00 00 00 ff  |................|\n" +
+		"...\n" +
+		"00000800  00 00 f7 00 00 00 00 ff  00 00 f8 00 00 00 00 ff  |................|\n" +
+		"00000810  00 00 f9 00 00 00 00 ff  00 00 fa 00 00 00 00 ff  |................|\n" +
+		"00000820  00 00 fb 00 00 00 00 ff  00 00 fc 00 00 00 00 ee  |................|\n" +
+		"00000830  d9 10 00 00 00 00 01 ff  db 10 00 00 00 00 01 ff  |................|\n" +
+		"00000840  e0 10 00 00 00 00 01 ff  d0 10 00 00 00 00 01 ff  |................|\n" +
+		"00000850  d3 10 00 00 00 00 01 ff  d6 10 00 00 00 00 01 ff  |................|\n" +
+		"00000860  dd 10 00 00 00 00 01 00  e2 10 00 00 00 00 01 01  |................|\n" +
+		"00000870  e5 10 00 00 00 00 01 ff  e8 10 00 00 00 00 01 ff  |................|\n" +
+		"...\n" +
+		"00001000  bb 13 00 00 00 00 01 ff  be 13 00 00 00 00 01 ff  |................|\n" +
+		"00001010  c1 13 00 00 00 00 01 ff  c4 13 00 00 00 00 01 ff  |................|\n" +
+		"00001020  c7 13 00 00 00 00 01 ff  e2 13 00 00 00 00 01 ff  |................|\n" +
+		"00001030  72 c3 63 09 f0 09 00 ff  00 00 00 00 00 00 00 ff  |r.c.............|\n" +
+		"00001040  00 00 01 00 00 00 00 ff  00 00 02 00 00 00 00 ff  |................|\n" +
+		"00001050  00 00 03 00 00 00 00 ff  00 00 04 00 00 00 00 ff  |................|\n" +
+		"00001060  00 00 05 00 00 00 00 ff  00 00 06 00 00 00 00 ff  |................|\n" +
+		"00001070  00 00 07 00 00 00 00 ff  00 00 08 00 00 00 00 ee  |................|\n" +
+		"00001080  db 10 00 00 00 00 01 ff  ca 13 00 00 00 00 01 ff  |................|\n" +
+		"00001090  cd 13 00 00 00 00 01 ff  d0 13 00 00 00 00 01 ff  |................|\n" +
+		"000010a0  d3 13 00 00 00 00 01 ff  d6 13 00 00 00 00 01 ff  |................|\n" +
+		"000010b0  d9 13 00 00 00 00 01 ff  dc 13 00 00 00 00 01 ff  |................|\n" +
+		"000010c0  df 13 00 00 00 00 01 00  e2 13 00 00 00 00 01 09  |................|\n" +
+		"000010d0  70 30 30 70 30 31 70 30  32 58 58 59 59 70 30 33  |p00p01p02XXYYp03|\n" +
+		"000010e0  5a 5a 70 30 34 70 30 35  70 30 36 70 30 37 70 30  |ZZp04p05p06p07p0|\n" +
+		"000010f0  38 70 30 39 70 30 61 70  30 62 70 30 63 70 30 64  |8p09p0ap0bp0cp0d|\n" +
+		"...\n" +
+		"000013c0  38 70 66 39 70 66 61 70  66 62 70 66 63 70 66 64  |8pf9pfapfbpfcpfd|\n" +
+		"000013d0  70 66 65 70 66 66 70 30  30 70 30 31 70 30 32 70  |pfepffp00p01p02p|\n" +
+		"000013e0  30 33                                             |03|\n"
+
+	if got != want {
+		t.Fatalf("\ngot:\n%s\nwant:\n%s", got, want)
+	}
+}
+
+func TestWriter1000Chunks(t *testing.T) {
+	w := &Writer{
+		Writer: ioutil.Discard,
+		Codec:  fakeCodec,
+	}
+	data := make([]byte, 1)
+	res, _ := w.AddResource(data)
+	for i := 0; i < 1000; i++ {
+		if i == 2*255 {
+			_ = w.AddChunk(1, data, res, 0)
+		} else {
+			_ = w.AddChunk(1, data, 0, 0)
+		}
+	}
+	if err := w.Close(); err != nil {
+		t.Fatalf("Close: %v", err)
+	}
+}
diff --git a/lib/raczlib/raczlib.go b/lib/raczlib/raczlib.go
new file mode 100644
index 0000000..d41b8df
--- /dev/null
+++ b/lib/raczlib/raczlib.go
@@ -0,0 +1,41 @@
+// Copyright 2019 The Wuffs Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// ----------------
+
+// Package raczlib provides access to RAC (Random Access Compression) files
+// with the Zlib compression codec.
+//
+// The RAC specification is at
+// https://github.com/google/wuffs/blob/master/doc/spec/rac-spec.md
+package raczlib
+
+import (
+	"github.com/google/wuffs/lib/rac"
+)
+
+const (
+	// MaxSize is the maximum RAC file size (in both CSpace and DSpace).
+	MaxSize = rac.MaxSize
+)
+
+// IndexLocation is whether the index is at the start or end of the RAC file.
+//
+// See the RAC specification for further discussion.
+type IndexLocation = rac.IndexLocation
+
+const (
+	IndexLocationAtEnd   = rac.IndexLocationAtEnd
+	IndexLocationAtStart = rac.IndexLocationAtStart
+)
diff --git a/lib/raczlib/writer.go b/lib/raczlib/writer.go
new file mode 100644
index 0000000..00c4815
--- /dev/null
+++ b/lib/raczlib/writer.go
@@ -0,0 +1,470 @@
+// Copyright 2019 The Wuffs Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package raczlib
+
+// TODO: API for shared dictionaries.
+
+import (
+	"bytes"
+	"compress/zlib"
+	"errors"
+	"io"
+
+	"github.com/google/wuffs/lib/rac"
+	"github.com/google/wuffs/lib/zlibcut"
+)
+
+const (
+	defaultDChunkSize = 65536 // 64 KiB.
+
+	maxCChunkSize       = 1 << 30 // 1 GiB.
+	maxTargetDChunkSize = 1 << 31 // 2 GiB.
+)
+
+var (
+	errCChunkSizeIsTooSmall = errors.New("raczlib: CChunkSize is too small")
+	errInvalidWriter        = errors.New("raczlib: invalid Writer")
+	errWriterIsClosed       = errors.New("raczlib: Writer is closed")
+
+	errInternalShortCSize = errors.New("raczlib: internal error: short CSize")
+)
+
+func startingTargetDChunkSize(cChunkSize uint64) uint64 { return 2 * cChunkSize }
+
+// stripTrailingZeroes returns b shorn of any trailing '\x00' bytes. The RAC
+// file format will implicitly set any trailing zeroes.
+func stripTrailingZeroes(b []byte) []byte {
+	n := len(b)
+	for (n > 0) && (b[n-1] == 0) {
+		n--
+	}
+	return b[:n]
+}
+
+// writeBuffer is a sequence of bytes, split into two slices: prev[p:] and
+// curr. Together, they hold the tail (so far) of the stream of bytes given to
+// an io.Writer.
+//
+// Conceptually, it is the concatenation of those two slices, but the struct
+// holds two slices, not one, to minimize copying and memory allocation.
+//
+// The first slice, prev[p:], is those bytes from all previous Write calls that
+// have been copied to an internal buffer but not yet fully processed.
+//
+// The second slice, curr, is those bytes from the current Write call that have
+// not been processed.
+//
+// The underlying array that backs the prev slice is owned by the writeBuffer,
+// and is re-used to minimize memory allocations. The curr slice is a sub-slice
+// of the []byte passed to the Write method, and is owned by the caller, not by
+// the writeBuffer.
+//
+// As bytes are fully processed, prev[p:] shrinks (by incrementing "p += etc")
+// and after len(prev[p:]) hits zero, curr shrinks (by re-assigning "curr =
+// curr[etc:]"). The two mechanisms differ (e.g. there is a p int that offsets
+// prev but not a corresponding c int that offsets curr) because of the
+// difference in backing-array memory ownership, mentioned above.
+//
+// The intended usage is for an io.Writer's Write(data) method to first call
+// writeBuffer.extend(data), then a mixture of multiple writeBuffer.peek(n) and
+// writeBuffer.advance(n), and finally writeBuffer.compact(). Thus, before and
+// after every Write call, writeBuffer.curr is empty and writebuffer.p is 0.
+type writeBuffer struct {
+	prev []byte
+	curr []byte
+	p    int
+}
+
+// extend extends b's view of an io.Writer's incoming byte stream.
+func (b *writeBuffer) extend(curr []byte) {
+	if len(b.curr) != 0 {
+		panic("inconsistent writeBuffer state")
+	}
+	b.curr = curr
+}
+
+// length returns the total number of bytes available in b's buffers.
+func (b *writeBuffer) length() uint64 {
+	return uint64(len(b.prev)-b.p) + uint64(len(b.curr))
+}
+
+// peek returns the next m bytes in b's buffers, where m is the minimum of n
+// and b.length().
+//
+// The bytes are returned in two slices, not a single contiguous slice, in
+// order to minimize copying and memory allocation.
+func (b *writeBuffer) peek(n uint64) ([]byte, []byte) {
+	available := uint64(len(b.prev) - b.p)
+	if n <= available {
+		return b.prev[b.p : b.p+int(n)], nil
+	}
+	n -= available
+	if n <= uint64(len(b.curr)) {
+		return b.prev[b.p:], b.curr[:n]
+	}
+	return b.prev[b.p:], b.curr
+}
+
+// advance consumes the next n bytes.
+func (b *writeBuffer) advance(n uint64) {
+	available := uint64(len(b.prev) - b.p)
+	if n <= available {
+		b.p += int(n)
+		return
+	}
+	n -= available
+	b.curr = b.curr[n:]
+	b.p = len(b.prev)
+}
+
+// compact moves and copies any unprocessed bytes in b.prev and b.curr to be at
+// the start of b.prev.
+func (b *writeBuffer) compact() {
+	// Move any remaining bytes in prev to the front.
+	n := copy(b.prev, b.prev[b.p:])
+	b.prev = b.prev[:n]
+	b.p = 0
+	// Move any remaining bytes in curr to prev.
+	b.prev = append(b.prev, b.curr...)
+	b.curr = nil
+}
+
+// Writer provides a relatively simple way to write a RAC file (with the Zlib
+// compression codec) - one that is created starting from nothing, as opposed
+// to incrementally modifying an existing RAC file.
+//
+// Other packages may provide a more flexible (and more complicated) way to
+// write or append to RAC files, but that is out of scope of this package.
+//
+// Do not modify its exported fields after calling any of its methods.
+//
+// Writer implements the io.Writer interface.
+type Writer struct {
+	// Writer is where the RAC-encoded data is written to.
+	//
+	// Nil is an invalid value.
+	Writer io.Writer
+
+	// IndexLocation is whether the index is at the start or end of the RAC
+	// file.
+	//
+	// See the RAC specification for further discussion.
+	//
+	// The zero value of this field is IndexLocationAtEnd: a one pass encoding.
+	IndexLocation IndexLocation
+
+	// TempFile is a temporary file to use for a two pass encoding. The field
+	// name says "file" but it need not be a real file, in the operating system
+	// sense.
+	//
+	// For IndexLocationAtEnd, this must be nil. For IndexLocationAtStart, this
+	// must be non-nil.
+	//
+	// It does not need to implement io.Seeker, if it supports separate read
+	// and write positions (e.g. it is a bytes.Buffer). If it does implement
+	// io.Seeker (e.g. it is an os.File), its current position will be noted
+	// (via SeekCurrent), then it will be written to (via the
+	// raczlib.Writer.Write method), then its position will be reset (via
+	// SeekSet), then it will be read from (via the raczlib.Writer.Close
+	// method).
+	//
+	// The raczlib.Writer does not call TempFile.Close even if that method
+	// exists (e.g. the TempFile is an os.File). In that case, closing the
+	// temporary file (and deleting it) after the raczlib.Writer is closed is
+	// the responsibility of the raczlib.Writer caller, not the raczlib.Writer
+	// itself.
+	TempFile io.ReadWriter
+
+	// CPageSize guides padding the output to minimize the number of pages that
+	// each chunk occupies (in what the RAC spec calls CSpace).
+	//
+	// It must either be zero (which means no padding is inserted) or a power
+	// of 2, and no larger than MaxSize.
+	//
+	// For example, suppose that CSpace is partitioned into 1024-byte pages,
+	// that 1000 bytes have already been written to the output, and the next
+	// chunk is 1500 bytes long.
+	//
+	//   - With no padding (i.e. with CPageSize set to 0), this chunk will
+	//     occupy the half-open range [1000, 2500) in CSpace, which straddles
+	//     three 1024-byte pages: the page [0, 1024), the page [1024, 2048) and
+	//     the page [2048, 3072). Call those pages p0, p1 and p2.
+	//
+	//   - With padding (i.e. with CPageSize set to 1024), 24 bytes of zeroes
+	//     are first inserted so that this chunk occupies the half-open range
+	//     [1024, 2524) in CSpace, which straddles only two pages (p1 and p2).
+	//
+	// This concept is similar, but not identical, to alignment. Even with a
+	// non-zero CPageSize, chunk start offsets are not necessarily aligned to
+	// page boundaries. For example, suppose that the chunk size was only 1040
+	// bytes, not 1500 bytes. No padding will be inserted, as both [1000, 2040)
+	// and [1024, 2064) straddle two pages: either pages p0 and p1, or pages p1
+	// and p2.
+	//
+	// Nonetheless, if all chunks (or all but the final chunk) have a size
+	// equal to (or just under) a multiple of the page size, then in practice,
+	// each chunk's starting offset will be aligned to a page boundary.
+	CPageSize uint64
+
+	// CChunkSize is the compressed size (i.e. size in CSpace) of each chunk.
+	// The final chunk might be smaller than this.
+	//
+	// This field is ignored if DChunkSize is non-zero.
+	CChunkSize uint64
+
+	// DChunkSize is the uncompressed size (i.e. size in DSpace) of each chunk.
+	// The final chunk might be smaller than this.
+	//
+	// If both CChunkSize and DChunkSize are non-zero, DChunkSize takes
+	// precedence and CChunkSize is ignored.
+	//
+	// If both CChunkSize and DChunkSize are zero, a default DChunkSize value
+	// will be used.
+	DChunkSize uint64
+
+	// cChunkSize and dChunkSize are copies of CChunkSize and DChunkSize,
+	// validated during the initialize method. For example, if both CChunkSize
+	// and DChunkSize are zero, dChunkSize will be defaultDChunkSize.
+	cChunkSize uint64
+	dChunkSize uint64
+
+	// err is the first error encountered. It is sticky: once a non-nil error
+	// occurs, all public methods will return that error.
+	err error
+
+	racWriter  rac.Writer
+	zlibWriter *zlib.Writer
+
+	compressed   bytes.Buffer
+	uncompressed writeBuffer
+}
+
+func (w *Writer) initialize() error {
+	if w.err != nil {
+		return w.err
+	}
+	if w.racWriter.Writer != nil {
+		// We're already initialized.
+		return nil
+	}
+	if w.Writer == nil {
+		w.err = errInvalidWriter
+		return w.err
+	}
+
+	if w.DChunkSize > 0 {
+		w.dChunkSize = w.DChunkSize
+	} else if w.CChunkSize > 0 {
+		w.cChunkSize = w.CChunkSize
+		if w.cChunkSize > maxCChunkSize {
+			w.cChunkSize = maxCChunkSize
+		}
+	} else {
+		w.dChunkSize = defaultDChunkSize
+	}
+
+	w.racWriter.Writer = w.Writer
+	w.racWriter.Codec = rac.CodecZlib
+	w.racWriter.IndexLocation = w.IndexLocation
+	w.racWriter.TempFile = w.TempFile
+	w.racWriter.CPageSize = w.CPageSize
+
+	w.zlibWriter, w.err = zlib.NewWriterLevel(&w.compressed, zlib.BestCompression)
+	return w.err
+}
+
+func (w *Writer) compress(dBytes0 []byte, dBytes1 []byte) ([]byte, error) {
+	w.compressed.Reset()
+	w.zlibWriter.Reset(&w.compressed)
+	if len(dBytes0) > 0 {
+		if _, err := w.zlibWriter.Write(dBytes0); err != nil {
+			w.err = err
+			return nil, err
+		}
+	}
+	if len(dBytes1) > 0 {
+		if _, err := w.zlibWriter.Write(dBytes1); err != nil {
+			w.err = err
+			return nil, err
+		}
+	}
+	if err := w.zlibWriter.Close(); err != nil {
+		w.err = err
+		return nil, err
+	}
+	return w.compressed.Bytes(), nil
+}
+
+// Write implements io.Writer.
+func (w *Writer) Write(p []byte) (int, error) {
+	if err := w.initialize(); err != nil {
+		return 0, err
+	}
+	w.uncompressed.extend(p)
+	n, err := len(p), w.write(false)
+	w.uncompressed.compact()
+	if err != nil {
+		return 0, err
+	}
+	return n, nil
+}
+
+func (w *Writer) write(eof bool) error {
+	if w.dChunkSize > 0 {
+		return w.writeDChunks(eof)
+	}
+	return w.writeCChunks(eof)
+}
+
+func (w *Writer) writeDChunks(eof bool) error {
+	for {
+		peek0, peek1 := w.uncompressed.peek(w.dChunkSize)
+		dSize := uint64(len(peek0)) + uint64(len(peek1))
+		if dSize == 0 {
+			return nil
+		}
+		if !eof && (dSize < w.dChunkSize) {
+			return nil
+		}
+
+		peek1 = stripTrailingZeroes(peek1)
+		if len(peek1) == 0 {
+			peek0 = stripTrailingZeroes(peek0)
+		}
+		cBytes, err := w.compress(peek0, peek1)
+		if err != nil {
+			return err
+		}
+
+		if err := w.racWriter.AddChunk(dSize, cBytes, 0, 0); err != nil {
+			w.err = err
+			return err
+		}
+		w.uncompressed.advance(dSize)
+	}
+}
+
+func (w *Writer) writeCChunks(eof bool) error {
+	// Each outer loop iteration tries to write exactly one chunk.
+outer:
+	for {
+		// We don't know, a priori, how many w.uncompressed bytes are needed to
+		// produce a compressed chunk of size cChunkSize. We use a
+		// startingTargetDChunkSize that is a small multiple of cChunkSize, and
+		// keep doubling that until we build something at least as large as
+		// cChunkSize, then zlibcut it back to be exactly cChunkSize.
+		targetDChunkSize := uint64(maxTargetDChunkSize)
+		if !eof {
+			targetDChunkSize = startingTargetDChunkSize(w.cChunkSize)
+		}
+
+		if n := w.uncompressed.length(); n == 0 {
+			return nil
+		} else if !eof && (n < targetDChunkSize) {
+			return nil
+		}
+
+		// The inner loop keeps doubling the targetDChunkSize until it's big
+		// enough to produce at least cChunkSize bytes of compressed data.
+		for {
+			next := targetDChunkSize * 2
+			if next > maxTargetDChunkSize {
+				next = maxTargetDChunkSize
+			}
+			force := next <= targetDChunkSize
+
+			if err := w.tryCChunk(targetDChunkSize, force); err == nil {
+				continue outer
+			} else if err != errInternalShortCSize {
+				return err
+			}
+
+			if w.uncompressed.length() <= targetDChunkSize {
+				// Growing the targetDChunkSize isn't going to change anything.
+				return nil
+			}
+			targetDChunkSize = next
+		}
+	}
+}
+
+func (w *Writer) tryCChunk(targetDChunkSize uint64, force bool) error {
+	peek0, peek1 := w.uncompressed.peek(targetDChunkSize)
+	dSize := uint64(len(peek0)) + uint64(len(peek1))
+	cBytes, err := w.compress(peek0, peek1)
+	if err != nil {
+		return err
+	}
+
+	switch {
+	case uint64(len(cBytes)) < w.cChunkSize:
+		if !force {
+			return errInternalShortCSize
+		}
+		fallthrough
+	case uint64(len(cBytes)) == w.cChunkSize:
+		if err := w.racWriter.AddChunk(dSize, cBytes, 0, 0); err != nil {
+			w.err = err
+			return err
+		}
+		w.uncompressed.advance(dSize)
+		return nil
+	}
+
+	eLen, dLen, err := zlibcut.Cut(nil, cBytes, int(w.cChunkSize))
+	if err != nil {
+		w.err = err
+		return err
+	}
+	if dLen == 0 {
+		w.err = errCChunkSizeIsTooSmall
+		return w.err
+	}
+	if err := w.racWriter.AddChunk(uint64(dLen), cBytes[:eLen], 0, 0); err != nil {
+		w.err = err
+		return err
+	}
+	w.uncompressed.advance(uint64(dLen))
+	return nil
+}
+
+// Close writes the RAC index to w.Writer and marks that w accepts no further
+// method calls.
+//
+// For a one pass encoding, no further action is taken. For a two pass encoding
+// (i.e. IndexLocationAtStart), it then copies w.TempFile to w.Writer. Either
+// way, if this method returns nil error, the entirety of what was written to
+// w.Writer constitutes a valid RAC file.
+//
+// Closing the underlying w.Writer, w.TempFile or both is the responsibility of
+// the raczlib.Writer caller, not the raczlib.Writer itself.
+//
+// It is not necessary to call Close, e.g. if an earlier Write call returned a
+// non-nil error. Unlike an os.File, failing to call raczlib.Writer.Close will
+// not leak resources such as file descriptors.
+func (w *Writer) Close() error {
+	if err := w.initialize(); err != nil {
+		return err
+	}
+	if err := w.write(true); err != nil {
+		return err
+	}
+	if err := w.racWriter.Close(); err != nil {
+		w.err = err
+		return err
+	}
+	w.err = errWriterIsClosed
+	return nil
+}
diff --git a/lib/zlibcut/example_test.go b/lib/zlibcut/example_test.go
new file mode 100644
index 0000000..76f4f4f
--- /dev/null
+++ b/lib/zlibcut/example_test.go
@@ -0,0 +1,121 @@
+// Copyright 2019 The Wuffs Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package zlibcut_test
+
+import (
+	"bytes"
+	"compress/zlib"
+	"fmt"
+	"io/ioutil"
+	"strings"
+
+	"github.com/google/wuffs/lib/zlibcut"
+)
+
+func ExampleCut() {
+	const sonnet18 = "" +
+		"Shall I compare thee to a summer’s day?\n" +
+		"Thou art more lovely and more temperate.\n" +
+		"Rough winds do shake the darling buds of May,\n" +
+		"And summer’s lease hath all too short a date.\n" +
+		"Sometime too hot the eye of heaven shines,\n" +
+		"And often is his gold complexion dimmed;\n" +
+		"And every fair from fair sometime declines,\n" +
+		"By chance, or nature’s changing course, untrimmed;\n" +
+		"But thy eternal summer shall not fade,\n" +
+		"Nor lose possession of that fair thou ow’st,\n" +
+		"Nor shall death brag thou wand'rest in his shade,\n" +
+		"When in eternal lines to Time thou grow'st.\n" +
+		"So long as men can breathe, or eyes can see,\n" +
+		"So long lives this, and this gives life to thee.\n"
+
+	if n := len(sonnet18); n != 632 {
+		fmt.Printf("len(sonnet18): got %d, want 632", n)
+		return
+	}
+
+	// Compress the input text, sonnet18.
+	buffer := &bytes.Buffer{}
+	w := zlib.NewWriter(buffer)
+	w.Write([]byte(sonnet18))
+	w.Close()
+	compressed := buffer.Bytes()
+
+	// The exact length of the zlib-compressed form of sonnet18 depends on the
+	// compression algorithm used, which can change from version to version of
+	// the Go standard library. Nonetheless, for a 632 byte input, we expect
+	// the compressed form to be between 300 and 500 bytes.
+	if n := len(compressed); (n < 300) || (500 < n) {
+		fmt.Printf("len(compressed): got %d, want something in [300, 500]", n)
+		return
+	}
+
+	// Cut the 300-or-more bytes to be 200.
+	encodedLen, decodedLen, err := zlibcut.Cut(nil, compressed, 200)
+	if err != nil {
+		fmt.Printf("Cut: %v", err)
+		return
+	}
+
+	// The encodedLen should be equal to or just under the requested 200.
+	cut := compressed[:encodedLen]
+	if n := len(cut); (n < 190) || (200 < n) {
+		fmt.Printf("len(cut): got %d, want something in [190, 200]", n)
+		return
+	}
+
+	// At this point, a real program would write that cut slice somewhere. The
+	// rest of this example verifies that the cut data has the properties we
+	// expect, given the semantics of zlibcut.Cut.
+
+	// Uncompress the cut data. It should be a valid zlib-compressed stream, so
+	// no errors should be encountered.
+	r, err := zlib.NewReader(bytes.NewReader(cut))
+	if err != nil {
+		fmt.Printf("NewReader: %v", err)
+		return
+	}
+	uncompressed, err := ioutil.ReadAll(r)
+	if err != nil {
+		fmt.Printf("ReadAll: %v", err)
+		return
+	}
+	err = r.Close()
+	if err != nil {
+		fmt.Printf("Close: %v", err)
+		return
+	}
+
+	// The uncompressed form of the cut data should be a prefix (of length
+	// decodedLen) of the original input, sonnet18. Again, the exact length
+	// depends on the zlib compression algorithm, but uncompressing 200 or so
+	// bytes should give between 250 and 400 bytes.
+	if n := len(uncompressed); n != decodedLen {
+		fmt.Printf("len(uncompressed): got %d, want %d", n, decodedLen)
+		return
+	} else if (n < 250) || (400 < n) {
+		fmt.Printf("len(uncompressed): got %d, want something in [250, 400]", n)
+		return
+	} else if !strings.HasPrefix(sonnet18, string(uncompressed)) {
+		fmt.Printf("uncompressed was not a prefix of the original input")
+		return
+	}
+
+	// The first two lines of the sonnet take 83 bytes.
+	fmt.Println(string(uncompressed[:83]))
+	// Output:
+	// Shall I compare thee to a summer’s day?
+	// Thou art more lovely and more temperate.
+}
diff --git a/lib/zlibcut/zlibcut.go b/lib/zlibcut/zlibcut.go
index 4f7da1c..6554ea1 100644
--- a/lib/zlibcut/zlibcut.go
+++ b/lib/zlibcut/zlibcut.go
@@ -23,8 +23,6 @@
 package zlibcut
 
 import (
-	"bytes"
-	"compress/flate"
 	"errors"
 	"hash/adler32"
 	"io"
@@ -52,11 +50,16 @@
 // zlib-compressed data, assuming that encoded starts off containing valid
 // zlib-compressed data.
 //
+// If a nil error is returned, then encodedLen <= maxEncodedLen will hold.
+//
 // Decompressing that modified, shorter byte slice produces a prefix (of length
 // decodedLen) of the decompression of the original, longer byte slice.
 //
+// If w is non-nil, that prefix is also written to w. If a non-nil error is
+// returned, incomplete data might still be written to w.
+//
 // It does not necessarily return the largest possible decodedLen.
-func Cut(encoded []byte, maxEncodedLen int) (encodedLen int, decodedLen int, retErr error) {
+func Cut(w io.Writer, encoded []byte, maxEncodedLen int) (encodedLen int, decodedLen int, retErr error) {
 	if len(encoded) < 2 {
 		return 0, 0, errInvalidNotEnoughData
 	}
@@ -85,7 +88,15 @@
 		return 0, 0, errMaxEncodedLenTooSmall
 	}
 
+	hasher := adler32.New()
+	if w == nil {
+		w = hasher
+	} else {
+		w = io.MultiWriter(w, hasher)
+	}
+
 	encodedLen, decodedLen, err := flatecut.Cut(
+		w,
 		encoded[payloadStart:len(encoded)-4],
 		maxEncodedLen-payloadStart-4,
 	)
@@ -93,15 +104,7 @@
 		return 0, 0, err
 	}
 
-	w := adler32.New()
-	r := bytes.NewReader(encoded[payloadStart : payloadStart+encodedLen])
-	if n, err := io.Copy(w, flate.NewReader(r)); err != nil {
-		return 0, 0, err
-	} else if n != int64(decodedLen) {
-		return 0, 0, errInternalInconsistentDecodedLen
-	}
-
-	hash := w.Sum32()
+	hash := hasher.Sum32()
 	hashBytes := encoded[payloadStart+encodedLen : payloadStart+encodedLen+4]
 	hashBytes[0] = uint8(hash >> 24)
 	hashBytes[1] = uint8(hash >> 16)
diff --git a/release/c/wuffs-unsupported-snapshot.c b/release/c/wuffs-unsupported-snapshot.c
index dc00a84..964d7dd 100644
--- a/release/c/wuffs-unsupported-snapshot.c
+++ b/release/c/wuffs-unsupported-snapshot.c
@@ -191,8 +191,10 @@
 typedef const char* wuffs_base__status;
 
 extern const char* wuffs_base__warning__end_of_data;
+extern const char* wuffs_base__warning__metadata_reported;
 extern const char* wuffs_base__suspension__short_read;
 extern const char* wuffs_base__suspension__short_write;
+extern const char* wuffs_base__error__bad_i_o_position;
 extern const char* wuffs_base__error__bad_argument_length_too_short;
 extern const char* wuffs_base__error__bad_argument;
 extern const char* wuffs_base__error__bad_call_sequence;
@@ -207,6 +209,7 @@
 extern const char* wuffs_base__error__initialize_not_called;
 extern const char* wuffs_base__error__interleaved_coroutine_calls;
 extern const char* wuffs_base__error__not_enough_data;
+extern const char* wuffs_base__error__unsupported_option;
 extern const char* wuffs_base__error__too_much_data;
 
 static inline bool  //
@@ -236,6 +239,16 @@
 
 // --------
 
+// FourCC constants.
+
+// International Color Consortium Profile.
+#define WUFFS_BASE__FOURCC__ICCP 0x49434350
+
+// Extensible Metadata Platform.
+#define WUFFS_BASE__FOURCC__XMP 0x584D5020
+
+// --------
+
 // Flicks are a unit of time. One flick (frame-tick) is 1 / 705_600_000 of a
 // second. See https://github.com/OculusVR/Flicks
 typedef int64_t wuffs_base__flicks;
@@ -509,14 +522,14 @@
   uint32_t max_incl;
 
 #ifdef __cplusplus
-  inline bool is_empty();
-  inline bool equals(wuffs_base__range_ii_u32__struct s);
+  inline bool is_empty() const;
+  inline bool equals(wuffs_base__range_ii_u32__struct s) const;
   inline wuffs_base__range_ii_u32__struct intersect(
-      wuffs_base__range_ii_u32__struct s);
+      wuffs_base__range_ii_u32__struct s) const;
   inline wuffs_base__range_ii_u32__struct unite(
-      wuffs_base__range_ii_u32__struct s);
-  inline bool contains(uint32_t x);
-  inline bool contains_range(wuffs_base__range_ii_u32__struct s);
+      wuffs_base__range_ii_u32__struct s) const;
+  inline bool contains(uint32_t x) const;
+  inline bool contains_range(wuffs_base__range_ii_u32__struct s) const;
 #endif  // __cplusplus
 
 } wuffs_base__range_ii_u32;
@@ -530,12 +543,12 @@
 }
 
 static inline bool  //
-wuffs_base__range_ii_u32__is_empty(wuffs_base__range_ii_u32* r) {
+wuffs_base__range_ii_u32__is_empty(const wuffs_base__range_ii_u32* r) {
   return r->min_incl > r->max_incl;
 }
 
 static inline bool  //
-wuffs_base__range_ii_u32__equals(wuffs_base__range_ii_u32* r,
+wuffs_base__range_ii_u32__equals(const wuffs_base__range_ii_u32* r,
                                  wuffs_base__range_ii_u32 s) {
   return (r->min_incl == s.min_incl && r->max_incl == s.max_incl) ||
          (wuffs_base__range_ii_u32__is_empty(r) &&
@@ -543,7 +556,7 @@
 }
 
 static inline wuffs_base__range_ii_u32  //
-wuffs_base__range_ii_u32__intersect(wuffs_base__range_ii_u32* r,
+wuffs_base__range_ii_u32__intersect(const wuffs_base__range_ii_u32* r,
                                     wuffs_base__range_ii_u32 s) {
   wuffs_base__range_ii_u32 t;
   t.min_incl = wuffs_base__u32__max(r->min_incl, s.min_incl);
@@ -552,7 +565,7 @@
 }
 
 static inline wuffs_base__range_ii_u32  //
-wuffs_base__range_ii_u32__unite(wuffs_base__range_ii_u32* r,
+wuffs_base__range_ii_u32__unite(const wuffs_base__range_ii_u32* r,
                                 wuffs_base__range_ii_u32 s) {
   if (wuffs_base__range_ii_u32__is_empty(r)) {
     return s;
@@ -567,12 +580,13 @@
 }
 
 static inline bool  //
-wuffs_base__range_ii_u32__contains(wuffs_base__range_ii_u32* r, uint32_t x) {
+wuffs_base__range_ii_u32__contains(const wuffs_base__range_ii_u32* r,
+                                   uint32_t x) {
   return (r->min_incl <= x) && (x <= r->max_incl);
 }
 
 static inline bool  //
-wuffs_base__range_ii_u32__contains_range(wuffs_base__range_ii_u32* r,
+wuffs_base__range_ii_u32__contains_range(const wuffs_base__range_ii_u32* r,
                                          wuffs_base__range_ii_u32 s) {
   return wuffs_base__range_ii_u32__equals(
       &s, wuffs_base__range_ii_u32__intersect(r, s));
@@ -581,32 +595,32 @@
 #ifdef __cplusplus
 
 inline bool  //
-wuffs_base__range_ii_u32::is_empty() {
+wuffs_base__range_ii_u32::is_empty() const {
   return wuffs_base__range_ii_u32__is_empty(this);
 }
 
 inline bool  //
-wuffs_base__range_ii_u32::equals(wuffs_base__range_ii_u32 s) {
+wuffs_base__range_ii_u32::equals(wuffs_base__range_ii_u32 s) const {
   return wuffs_base__range_ii_u32__equals(this, s);
 }
 
 inline wuffs_base__range_ii_u32  //
-wuffs_base__range_ii_u32::intersect(wuffs_base__range_ii_u32 s) {
+wuffs_base__range_ii_u32::intersect(wuffs_base__range_ii_u32 s) const {
   return wuffs_base__range_ii_u32__intersect(this, s);
 }
 
 inline wuffs_base__range_ii_u32  //
-wuffs_base__range_ii_u32::unite(wuffs_base__range_ii_u32 s) {
+wuffs_base__range_ii_u32::unite(wuffs_base__range_ii_u32 s) const {
   return wuffs_base__range_ii_u32__unite(this, s);
 }
 
 inline bool  //
-wuffs_base__range_ii_u32::contains(uint32_t x) {
+wuffs_base__range_ii_u32::contains(uint32_t x) const {
   return wuffs_base__range_ii_u32__contains(this, x);
 }
 
 inline bool  //
-wuffs_base__range_ii_u32::contains_range(wuffs_base__range_ii_u32 s) {
+wuffs_base__range_ii_u32::contains_range(wuffs_base__range_ii_u32 s) const {
   return wuffs_base__range_ii_u32__contains_range(this, s);
 }
 
@@ -619,15 +633,15 @@
   uint32_t max_excl;
 
 #ifdef __cplusplus
-  inline bool is_empty();
-  inline bool equals(wuffs_base__range_ie_u32__struct s);
+  inline bool is_empty() const;
+  inline bool equals(wuffs_base__range_ie_u32__struct s) const;
   inline wuffs_base__range_ie_u32__struct intersect(
-      wuffs_base__range_ie_u32__struct s);
+      wuffs_base__range_ie_u32__struct s) const;
   inline wuffs_base__range_ie_u32__struct unite(
-      wuffs_base__range_ie_u32__struct s);
-  inline bool contains(uint32_t x);
-  inline bool contains_range(wuffs_base__range_ie_u32__struct s);
-  inline uint32_t length();
+      wuffs_base__range_ie_u32__struct s) const;
+  inline bool contains(uint32_t x) const;
+  inline bool contains_range(wuffs_base__range_ie_u32__struct s) const;
+  inline uint32_t length() const;
 #endif  // __cplusplus
 
 } wuffs_base__range_ie_u32;
@@ -641,12 +655,12 @@
 }
 
 static inline bool  //
-wuffs_base__range_ie_u32__is_empty(wuffs_base__range_ie_u32* r) {
+wuffs_base__range_ie_u32__is_empty(const wuffs_base__range_ie_u32* r) {
   return r->min_incl >= r->max_excl;
 }
 
 static inline bool  //
-wuffs_base__range_ie_u32__equals(wuffs_base__range_ie_u32* r,
+wuffs_base__range_ie_u32__equals(const wuffs_base__range_ie_u32* r,
                                  wuffs_base__range_ie_u32 s) {
   return (r->min_incl == s.min_incl && r->max_excl == s.max_excl) ||
          (wuffs_base__range_ie_u32__is_empty(r) &&
@@ -654,7 +668,7 @@
 }
 
 static inline wuffs_base__range_ie_u32  //
-wuffs_base__range_ie_u32__intersect(wuffs_base__range_ie_u32* r,
+wuffs_base__range_ie_u32__intersect(const wuffs_base__range_ie_u32* r,
                                     wuffs_base__range_ie_u32 s) {
   wuffs_base__range_ie_u32 t;
   t.min_incl = wuffs_base__u32__max(r->min_incl, s.min_incl);
@@ -663,7 +677,7 @@
 }
 
 static inline wuffs_base__range_ie_u32  //
-wuffs_base__range_ie_u32__unite(wuffs_base__range_ie_u32* r,
+wuffs_base__range_ie_u32__unite(const wuffs_base__range_ie_u32* r,
                                 wuffs_base__range_ie_u32 s) {
   if (wuffs_base__range_ie_u32__is_empty(r)) {
     return s;
@@ -678,56 +692,57 @@
 }
 
 static inline bool  //
-wuffs_base__range_ie_u32__contains(wuffs_base__range_ie_u32* r, uint32_t x) {
+wuffs_base__range_ie_u32__contains(const wuffs_base__range_ie_u32* r,
+                                   uint32_t x) {
   return (r->min_incl <= x) && (x < r->max_excl);
 }
 
 static inline bool  //
-wuffs_base__range_ie_u32__contains_range(wuffs_base__range_ie_u32* r,
+wuffs_base__range_ie_u32__contains_range(const wuffs_base__range_ie_u32* r,
                                          wuffs_base__range_ie_u32 s) {
   return wuffs_base__range_ie_u32__equals(
       &s, wuffs_base__range_ie_u32__intersect(r, s));
 }
 
 static inline uint32_t  //
-wuffs_base__range_ie_u32__length(wuffs_base__range_ie_u32* r) {
+wuffs_base__range_ie_u32__length(const wuffs_base__range_ie_u32* r) {
   return wuffs_base__u32__sat_sub(r->max_excl, r->min_incl);
 }
 
 #ifdef __cplusplus
 
 inline bool  //
-wuffs_base__range_ie_u32::is_empty() {
+wuffs_base__range_ie_u32::is_empty() const {
   return wuffs_base__range_ie_u32__is_empty(this);
 }
 
 inline bool  //
-wuffs_base__range_ie_u32::equals(wuffs_base__range_ie_u32 s) {
+wuffs_base__range_ie_u32::equals(wuffs_base__range_ie_u32 s) const {
   return wuffs_base__range_ie_u32__equals(this, s);
 }
 
 inline wuffs_base__range_ie_u32  //
-wuffs_base__range_ie_u32::intersect(wuffs_base__range_ie_u32 s) {
+wuffs_base__range_ie_u32::intersect(wuffs_base__range_ie_u32 s) const {
   return wuffs_base__range_ie_u32__intersect(this, s);
 }
 
 inline wuffs_base__range_ie_u32  //
-wuffs_base__range_ie_u32::unite(wuffs_base__range_ie_u32 s) {
+wuffs_base__range_ie_u32::unite(wuffs_base__range_ie_u32 s) const {
   return wuffs_base__range_ie_u32__unite(this, s);
 }
 
 inline bool  //
-wuffs_base__range_ie_u32::contains(uint32_t x) {
+wuffs_base__range_ie_u32::contains(uint32_t x) const {
   return wuffs_base__range_ie_u32__contains(this, x);
 }
 
 inline bool  //
-wuffs_base__range_ie_u32::contains_range(wuffs_base__range_ie_u32 s) {
+wuffs_base__range_ie_u32::contains_range(wuffs_base__range_ie_u32 s) const {
   return wuffs_base__range_ie_u32__contains_range(this, s);
 }
 
 inline uint32_t  //
-wuffs_base__range_ie_u32::length() {
+wuffs_base__range_ie_u32::length() const {
   return wuffs_base__range_ie_u32__length(this);
 }
 
@@ -740,14 +755,14 @@
   uint64_t max_incl;
 
 #ifdef __cplusplus
-  inline bool is_empty();
-  inline bool equals(wuffs_base__range_ii_u64__struct s);
+  inline bool is_empty() const;
+  inline bool equals(wuffs_base__range_ii_u64__struct s) const;
   inline wuffs_base__range_ii_u64__struct intersect(
-      wuffs_base__range_ii_u64__struct s);
+      wuffs_base__range_ii_u64__struct s) const;
   inline wuffs_base__range_ii_u64__struct unite(
-      wuffs_base__range_ii_u64__struct s);
-  inline bool contains(uint64_t x);
-  inline bool contains_range(wuffs_base__range_ii_u64__struct s);
+      wuffs_base__range_ii_u64__struct s) const;
+  inline bool contains(uint64_t x) const;
+  inline bool contains_range(wuffs_base__range_ii_u64__struct s) const;
 #endif  // __cplusplus
 
 } wuffs_base__range_ii_u64;
@@ -761,12 +776,12 @@
 }
 
 static inline bool  //
-wuffs_base__range_ii_u64__is_empty(wuffs_base__range_ii_u64* r) {
+wuffs_base__range_ii_u64__is_empty(const wuffs_base__range_ii_u64* r) {
   return r->min_incl > r->max_incl;
 }
 
 static inline bool  //
-wuffs_base__range_ii_u64__equals(wuffs_base__range_ii_u64* r,
+wuffs_base__range_ii_u64__equals(const wuffs_base__range_ii_u64* r,
                                  wuffs_base__range_ii_u64 s) {
   return (r->min_incl == s.min_incl && r->max_incl == s.max_incl) ||
          (wuffs_base__range_ii_u64__is_empty(r) &&
@@ -774,7 +789,7 @@
 }
 
 static inline wuffs_base__range_ii_u64  //
-wuffs_base__range_ii_u64__intersect(wuffs_base__range_ii_u64* r,
+wuffs_base__range_ii_u64__intersect(const wuffs_base__range_ii_u64* r,
                                     wuffs_base__range_ii_u64 s) {
   wuffs_base__range_ii_u64 t;
   t.min_incl = wuffs_base__u64__max(r->min_incl, s.min_incl);
@@ -783,7 +798,7 @@
 }
 
 static inline wuffs_base__range_ii_u64  //
-wuffs_base__range_ii_u64__unite(wuffs_base__range_ii_u64* r,
+wuffs_base__range_ii_u64__unite(const wuffs_base__range_ii_u64* r,
                                 wuffs_base__range_ii_u64 s) {
   if (wuffs_base__range_ii_u64__is_empty(r)) {
     return s;
@@ -798,12 +813,13 @@
 }
 
 static inline bool  //
-wuffs_base__range_ii_u64__contains(wuffs_base__range_ii_u64* r, uint64_t x) {
+wuffs_base__range_ii_u64__contains(const wuffs_base__range_ii_u64* r,
+                                   uint64_t x) {
   return (r->min_incl <= x) && (x <= r->max_incl);
 }
 
 static inline bool  //
-wuffs_base__range_ii_u64__contains_range(wuffs_base__range_ii_u64* r,
+wuffs_base__range_ii_u64__contains_range(const wuffs_base__range_ii_u64* r,
                                          wuffs_base__range_ii_u64 s) {
   return wuffs_base__range_ii_u64__equals(
       &s, wuffs_base__range_ii_u64__intersect(r, s));
@@ -812,32 +828,32 @@
 #ifdef __cplusplus
 
 inline bool  //
-wuffs_base__range_ii_u64::is_empty() {
+wuffs_base__range_ii_u64::is_empty() const {
   return wuffs_base__range_ii_u64__is_empty(this);
 }
 
 inline bool  //
-wuffs_base__range_ii_u64::equals(wuffs_base__range_ii_u64 s) {
+wuffs_base__range_ii_u64::equals(wuffs_base__range_ii_u64 s) const {
   return wuffs_base__range_ii_u64__equals(this, s);
 }
 
 inline wuffs_base__range_ii_u64  //
-wuffs_base__range_ii_u64::intersect(wuffs_base__range_ii_u64 s) {
+wuffs_base__range_ii_u64::intersect(wuffs_base__range_ii_u64 s) const {
   return wuffs_base__range_ii_u64__intersect(this, s);
 }
 
 inline wuffs_base__range_ii_u64  //
-wuffs_base__range_ii_u64::unite(wuffs_base__range_ii_u64 s) {
+wuffs_base__range_ii_u64::unite(wuffs_base__range_ii_u64 s) const {
   return wuffs_base__range_ii_u64__unite(this, s);
 }
 
 inline bool  //
-wuffs_base__range_ii_u64::contains(uint64_t x) {
+wuffs_base__range_ii_u64::contains(uint64_t x) const {
   return wuffs_base__range_ii_u64__contains(this, x);
 }
 
 inline bool  //
-wuffs_base__range_ii_u64::contains_range(wuffs_base__range_ii_u64 s) {
+wuffs_base__range_ii_u64::contains_range(wuffs_base__range_ii_u64 s) const {
   return wuffs_base__range_ii_u64__contains_range(this, s);
 }
 
@@ -850,15 +866,15 @@
   uint64_t max_excl;
 
 #ifdef __cplusplus
-  inline bool is_empty();
-  inline bool equals(wuffs_base__range_ie_u64__struct s);
+  inline bool is_empty() const;
+  inline bool equals(wuffs_base__range_ie_u64__struct s) const;
   inline wuffs_base__range_ie_u64__struct intersect(
-      wuffs_base__range_ie_u64__struct s);
+      wuffs_base__range_ie_u64__struct s) const;
   inline wuffs_base__range_ie_u64__struct unite(
-      wuffs_base__range_ie_u64__struct s);
-  inline bool contains(uint64_t x);
-  inline bool contains_range(wuffs_base__range_ie_u64__struct s);
-  inline uint64_t length();
+      wuffs_base__range_ie_u64__struct s) const;
+  inline bool contains(uint64_t x) const;
+  inline bool contains_range(wuffs_base__range_ie_u64__struct s) const;
+  inline uint64_t length() const;
 #endif  // __cplusplus
 
 } wuffs_base__range_ie_u64;
@@ -872,12 +888,12 @@
 }
 
 static inline bool  //
-wuffs_base__range_ie_u64__is_empty(wuffs_base__range_ie_u64* r) {
+wuffs_base__range_ie_u64__is_empty(const wuffs_base__range_ie_u64* r) {
   return r->min_incl >= r->max_excl;
 }
 
 static inline bool  //
-wuffs_base__range_ie_u64__equals(wuffs_base__range_ie_u64* r,
+wuffs_base__range_ie_u64__equals(const wuffs_base__range_ie_u64* r,
                                  wuffs_base__range_ie_u64 s) {
   return (r->min_incl == s.min_incl && r->max_excl == s.max_excl) ||
          (wuffs_base__range_ie_u64__is_empty(r) &&
@@ -885,7 +901,7 @@
 }
 
 static inline wuffs_base__range_ie_u64  //
-wuffs_base__range_ie_u64__intersect(wuffs_base__range_ie_u64* r,
+wuffs_base__range_ie_u64__intersect(const wuffs_base__range_ie_u64* r,
                                     wuffs_base__range_ie_u64 s) {
   wuffs_base__range_ie_u64 t;
   t.min_incl = wuffs_base__u64__max(r->min_incl, s.min_incl);
@@ -894,7 +910,7 @@
 }
 
 static inline wuffs_base__range_ie_u64  //
-wuffs_base__range_ie_u64__unite(wuffs_base__range_ie_u64* r,
+wuffs_base__range_ie_u64__unite(const wuffs_base__range_ie_u64* r,
                                 wuffs_base__range_ie_u64 s) {
   if (wuffs_base__range_ie_u64__is_empty(r)) {
     return s;
@@ -909,56 +925,57 @@
 }
 
 static inline bool  //
-wuffs_base__range_ie_u64__contains(wuffs_base__range_ie_u64* r, uint64_t x) {
+wuffs_base__range_ie_u64__contains(const wuffs_base__range_ie_u64* r,
+                                   uint64_t x) {
   return (r->min_incl <= x) && (x < r->max_excl);
 }
 
 static inline bool  //
-wuffs_base__range_ie_u64__contains_range(wuffs_base__range_ie_u64* r,
+wuffs_base__range_ie_u64__contains_range(const wuffs_base__range_ie_u64* r,
                                          wuffs_base__range_ie_u64 s) {
   return wuffs_base__range_ie_u64__equals(
       &s, wuffs_base__range_ie_u64__intersect(r, s));
 }
 
 static inline uint64_t  //
-wuffs_base__range_ie_u64__length(wuffs_base__range_ie_u64* r) {
+wuffs_base__range_ie_u64__length(const wuffs_base__range_ie_u64* r) {
   return wuffs_base__u64__sat_sub(r->max_excl, r->min_incl);
 }
 
 #ifdef __cplusplus
 
 inline bool  //
-wuffs_base__range_ie_u64::is_empty() {
+wuffs_base__range_ie_u64::is_empty() const {
   return wuffs_base__range_ie_u64__is_empty(this);
 }
 
 inline bool  //
-wuffs_base__range_ie_u64::equals(wuffs_base__range_ie_u64 s) {
+wuffs_base__range_ie_u64::equals(wuffs_base__range_ie_u64 s) const {
   return wuffs_base__range_ie_u64__equals(this, s);
 }
 
 inline wuffs_base__range_ie_u64  //
-wuffs_base__range_ie_u64::intersect(wuffs_base__range_ie_u64 s) {
+wuffs_base__range_ie_u64::intersect(wuffs_base__range_ie_u64 s) const {
   return wuffs_base__range_ie_u64__intersect(this, s);
 }
 
 inline wuffs_base__range_ie_u64  //
-wuffs_base__range_ie_u64::unite(wuffs_base__range_ie_u64 s) {
+wuffs_base__range_ie_u64::unite(wuffs_base__range_ie_u64 s) const {
   return wuffs_base__range_ie_u64__unite(this, s);
 }
 
 inline bool  //
-wuffs_base__range_ie_u64::contains(uint64_t x) {
+wuffs_base__range_ie_u64::contains(uint64_t x) const {
   return wuffs_base__range_ie_u64__contains(this, x);
 }
 
 inline bool  //
-wuffs_base__range_ie_u64::contains_range(wuffs_base__range_ie_u64 s) {
+wuffs_base__range_ie_u64::contains_range(wuffs_base__range_ie_u64 s) const {
   return wuffs_base__range_ie_u64__contains_range(this, s);
 }
 
 inline uint64_t  //
-wuffs_base__range_ie_u64::length() {
+wuffs_base__range_ie_u64::length() const {
   return wuffs_base__range_ie_u64__length(this);
 }
 
@@ -982,14 +999,14 @@
   uint32_t max_incl_y;
 
 #ifdef __cplusplus
-  inline bool is_empty();
-  inline bool equals(wuffs_base__rect_ii_u32__struct s);
+  inline bool is_empty() const;
+  inline bool equals(wuffs_base__rect_ii_u32__struct s) const;
   inline wuffs_base__rect_ii_u32__struct intersect(
-      wuffs_base__rect_ii_u32__struct s);
+      wuffs_base__rect_ii_u32__struct s) const;
   inline wuffs_base__rect_ii_u32__struct unite(
-      wuffs_base__rect_ii_u32__struct s);
-  inline bool contains(uint32_t x, uint32_t y);
-  inline bool contains_rect(wuffs_base__rect_ii_u32__struct s);
+      wuffs_base__rect_ii_u32__struct s) const;
+  inline bool contains(uint32_t x, uint32_t y) const;
+  inline bool contains_rect(wuffs_base__rect_ii_u32__struct s) const;
 #endif  // __cplusplus
 
 } wuffs_base__rect_ii_u32;
@@ -1008,12 +1025,12 @@
 }
 
 static inline bool  //
-wuffs_base__rect_ii_u32__is_empty(wuffs_base__rect_ii_u32* r) {
+wuffs_base__rect_ii_u32__is_empty(const wuffs_base__rect_ii_u32* r) {
   return (r->min_incl_x > r->max_incl_x) || (r->min_incl_y > r->max_incl_y);
 }
 
 static inline bool  //
-wuffs_base__rect_ii_u32__equals(wuffs_base__rect_ii_u32* r,
+wuffs_base__rect_ii_u32__equals(const wuffs_base__rect_ii_u32* r,
                                 wuffs_base__rect_ii_u32 s) {
   return (r->min_incl_x == s.min_incl_x && r->min_incl_y == s.min_incl_y &&
           r->max_incl_x == s.max_incl_x && r->max_incl_y == s.max_incl_y) ||
@@ -1022,7 +1039,7 @@
 }
 
 static inline wuffs_base__rect_ii_u32  //
-wuffs_base__rect_ii_u32__intersect(wuffs_base__rect_ii_u32* r,
+wuffs_base__rect_ii_u32__intersect(const wuffs_base__rect_ii_u32* r,
                                    wuffs_base__rect_ii_u32 s) {
   wuffs_base__rect_ii_u32 t;
   t.min_incl_x = wuffs_base__u32__max(r->min_incl_x, s.min_incl_x);
@@ -1033,7 +1050,7 @@
 }
 
 static inline wuffs_base__rect_ii_u32  //
-wuffs_base__rect_ii_u32__unite(wuffs_base__rect_ii_u32* r,
+wuffs_base__rect_ii_u32__unite(const wuffs_base__rect_ii_u32* r,
                                wuffs_base__rect_ii_u32 s) {
   if (wuffs_base__rect_ii_u32__is_empty(r)) {
     return s;
@@ -1050,7 +1067,7 @@
 }
 
 static inline bool  //
-wuffs_base__rect_ii_u32__contains(wuffs_base__rect_ii_u32* r,
+wuffs_base__rect_ii_u32__contains(const wuffs_base__rect_ii_u32* r,
                                   uint32_t x,
                                   uint32_t y) {
   return (r->min_incl_x <= x) && (x <= r->max_incl_x) && (r->min_incl_y <= y) &&
@@ -1058,7 +1075,7 @@
 }
 
 static inline bool  //
-wuffs_base__rect_ii_u32__contains_rect(wuffs_base__rect_ii_u32* r,
+wuffs_base__rect_ii_u32__contains_rect(const wuffs_base__rect_ii_u32* r,
                                        wuffs_base__rect_ii_u32 s) {
   return wuffs_base__rect_ii_u32__equals(
       &s, wuffs_base__rect_ii_u32__intersect(r, s));
@@ -1067,32 +1084,32 @@
 #ifdef __cplusplus
 
 inline bool  //
-wuffs_base__rect_ii_u32::is_empty() {
+wuffs_base__rect_ii_u32::is_empty() const {
   return wuffs_base__rect_ii_u32__is_empty(this);
 }
 
 inline bool  //
-wuffs_base__rect_ii_u32::equals(wuffs_base__rect_ii_u32 s) {
+wuffs_base__rect_ii_u32::equals(wuffs_base__rect_ii_u32 s) const {
   return wuffs_base__rect_ii_u32__equals(this, s);
 }
 
 inline wuffs_base__rect_ii_u32  //
-wuffs_base__rect_ii_u32::intersect(wuffs_base__rect_ii_u32 s) {
+wuffs_base__rect_ii_u32::intersect(wuffs_base__rect_ii_u32 s) const {
   return wuffs_base__rect_ii_u32__intersect(this, s);
 }
 
 inline wuffs_base__rect_ii_u32  //
-wuffs_base__rect_ii_u32::unite(wuffs_base__rect_ii_u32 s) {
+wuffs_base__rect_ii_u32::unite(wuffs_base__rect_ii_u32 s) const {
   return wuffs_base__rect_ii_u32__unite(this, s);
 }
 
 inline bool  //
-wuffs_base__rect_ii_u32::contains(uint32_t x, uint32_t y) {
+wuffs_base__rect_ii_u32::contains(uint32_t x, uint32_t y) const {
   return wuffs_base__rect_ii_u32__contains(this, x, y);
 }
 
 inline bool  //
-wuffs_base__rect_ii_u32::contains_rect(wuffs_base__rect_ii_u32 s) {
+wuffs_base__rect_ii_u32::contains_rect(wuffs_base__rect_ii_u32 s) const {
   return wuffs_base__rect_ii_u32__contains_rect(this, s);
 }
 
@@ -1117,16 +1134,16 @@
   uint32_t max_excl_y;
 
 #ifdef __cplusplus
-  inline bool is_empty();
-  inline bool equals(wuffs_base__rect_ie_u32__struct s);
+  inline bool is_empty() const;
+  inline bool equals(wuffs_base__rect_ie_u32__struct s) const;
   inline wuffs_base__rect_ie_u32__struct intersect(
-      wuffs_base__rect_ie_u32__struct s);
+      wuffs_base__rect_ie_u32__struct s) const;
   inline wuffs_base__rect_ie_u32__struct unite(
-      wuffs_base__rect_ie_u32__struct s);
-  inline bool contains(uint32_t x, uint32_t y);
-  inline bool contains_rect(wuffs_base__rect_ie_u32__struct s);
-  inline uint32_t width();
-  inline uint32_t height();
+      wuffs_base__rect_ie_u32__struct s) const;
+  inline bool contains(uint32_t x, uint32_t y) const;
+  inline bool contains_rect(wuffs_base__rect_ie_u32__struct s) const;
+  inline uint32_t width() const;
+  inline uint32_t height() const;
 #endif  // __cplusplus
 
 } wuffs_base__rect_ie_u32;
@@ -1145,12 +1162,12 @@
 }
 
 static inline bool  //
-wuffs_base__rect_ie_u32__is_empty(wuffs_base__rect_ie_u32* r) {
+wuffs_base__rect_ie_u32__is_empty(const wuffs_base__rect_ie_u32* r) {
   return (r->min_incl_x >= r->max_excl_x) || (r->min_incl_y >= r->max_excl_y);
 }
 
 static inline bool  //
-wuffs_base__rect_ie_u32__equals(wuffs_base__rect_ie_u32* r,
+wuffs_base__rect_ie_u32__equals(const wuffs_base__rect_ie_u32* r,
                                 wuffs_base__rect_ie_u32 s) {
   return (r->min_incl_x == s.min_incl_x && r->min_incl_y == s.min_incl_y &&
           r->max_excl_x == s.max_excl_x && r->max_excl_y == s.max_excl_y) ||
@@ -1159,7 +1176,7 @@
 }
 
 static inline wuffs_base__rect_ie_u32  //
-wuffs_base__rect_ie_u32__intersect(wuffs_base__rect_ie_u32* r,
+wuffs_base__rect_ie_u32__intersect(const wuffs_base__rect_ie_u32* r,
                                    wuffs_base__rect_ie_u32 s) {
   wuffs_base__rect_ie_u32 t;
   t.min_incl_x = wuffs_base__u32__max(r->min_incl_x, s.min_incl_x);
@@ -1170,7 +1187,7 @@
 }
 
 static inline wuffs_base__rect_ie_u32  //
-wuffs_base__rect_ie_u32__unite(wuffs_base__rect_ie_u32* r,
+wuffs_base__rect_ie_u32__unite(const wuffs_base__rect_ie_u32* r,
                                wuffs_base__rect_ie_u32 s) {
   if (wuffs_base__rect_ie_u32__is_empty(r)) {
     return s;
@@ -1187,7 +1204,7 @@
 }
 
 static inline bool  //
-wuffs_base__rect_ie_u32__contains(wuffs_base__rect_ie_u32* r,
+wuffs_base__rect_ie_u32__contains(const wuffs_base__rect_ie_u32* r,
                                   uint32_t x,
                                   uint32_t y) {
   return (r->min_incl_x <= x) && (x < r->max_excl_x) && (r->min_incl_y <= y) &&
@@ -1195,95 +1212,69 @@
 }
 
 static inline bool  //
-wuffs_base__rect_ie_u32__contains_rect(wuffs_base__rect_ie_u32* r,
+wuffs_base__rect_ie_u32__contains_rect(const wuffs_base__rect_ie_u32* r,
                                        wuffs_base__rect_ie_u32 s) {
   return wuffs_base__rect_ie_u32__equals(
       &s, wuffs_base__rect_ie_u32__intersect(r, s));
 }
 
 static inline uint32_t  //
-wuffs_base__rect_ie_u32__width(wuffs_base__rect_ie_u32* r) {
+wuffs_base__rect_ie_u32__width(const wuffs_base__rect_ie_u32* r) {
   return wuffs_base__u32__sat_sub(r->max_excl_x, r->min_incl_x);
 }
 
 static inline uint32_t  //
-wuffs_base__rect_ie_u32__height(wuffs_base__rect_ie_u32* r) {
+wuffs_base__rect_ie_u32__height(const wuffs_base__rect_ie_u32* r) {
   return wuffs_base__u32__sat_sub(r->max_excl_y, r->min_incl_y);
 }
 
 #ifdef __cplusplus
 
 inline bool  //
-wuffs_base__rect_ie_u32::is_empty() {
+wuffs_base__rect_ie_u32::is_empty() const {
   return wuffs_base__rect_ie_u32__is_empty(this);
 }
 
 inline bool  //
-wuffs_base__rect_ie_u32::equals(wuffs_base__rect_ie_u32 s) {
+wuffs_base__rect_ie_u32::equals(wuffs_base__rect_ie_u32 s) const {
   return wuffs_base__rect_ie_u32__equals(this, s);
 }
 
 inline wuffs_base__rect_ie_u32  //
-wuffs_base__rect_ie_u32::intersect(wuffs_base__rect_ie_u32 s) {
+wuffs_base__rect_ie_u32::intersect(wuffs_base__rect_ie_u32 s) const {
   return wuffs_base__rect_ie_u32__intersect(this, s);
 }
 
 inline wuffs_base__rect_ie_u32  //
-wuffs_base__rect_ie_u32::unite(wuffs_base__rect_ie_u32 s) {
+wuffs_base__rect_ie_u32::unite(wuffs_base__rect_ie_u32 s) const {
   return wuffs_base__rect_ie_u32__unite(this, s);
 }
 
 inline bool  //
-wuffs_base__rect_ie_u32::contains(uint32_t x, uint32_t y) {
+wuffs_base__rect_ie_u32::contains(uint32_t x, uint32_t y) const {
   return wuffs_base__rect_ie_u32__contains(this, x, y);
 }
 
 inline bool  //
-wuffs_base__rect_ie_u32::contains_rect(wuffs_base__rect_ie_u32 s) {
+wuffs_base__rect_ie_u32::contains_rect(wuffs_base__rect_ie_u32 s) const {
   return wuffs_base__rect_ie_u32__contains_rect(this, s);
 }
 
 inline uint32_t  //
-wuffs_base__rect_ie_u32::width() {
+wuffs_base__rect_ie_u32::width() const {
   return wuffs_base__rect_ie_u32__width(this);
 }
 
 inline uint32_t  //
-wuffs_base__rect_ie_u32::height() {
+wuffs_base__rect_ie_u32::height() const {
   return wuffs_base__rect_ie_u32__height(this);
 }
 
 #endif  // __cplusplus
 
 // ---------------- I/O
-
-struct wuffs_base__io_buffer__struct;
-
-typedef struct {
-  // Do not access the private_impl's fields directly. There is no API/ABI
-  // compatibility or safety guarantee if you do so.
-  struct {
-    struct wuffs_base__io_buffer__struct* buf;
-    // The bounds values are typically NULL, when created by the Wuffs public
-    // API. NULL means that the callee substitutes the implicit bounds derived
-    // from buf.
-    uint8_t* mark;
-    uint8_t* limit;
-  } private_impl;
-} wuffs_base__io_reader;
-
-typedef struct {
-  // Do not access the private_impl's fields directly. There is no API/ABI
-  // compatibility or safety guarantee if you do so.
-  struct {
-    struct wuffs_base__io_buffer__struct* buf;
-    // The bounds values are typically NULL, when created by the Wuffs public
-    // API. NULL means that the callee substitutes the implicit bounds derived
-    // from buf.
-    uint8_t* mark;
-    uint8_t* limit;
-  } private_impl;
-} wuffs_base__io_writer;
+//
+// See (/doc/note/io-input-output.md).
 
 // wuffs_base__io_buffer_meta is the metadata for a wuffs_base__io_buffer's
 // data.
@@ -1304,10 +1295,12 @@
 
 #ifdef __cplusplus
   inline void compact();
-  inline wuffs_base__io_reader reader();
-  inline wuffs_base__io_writer writer();
-  inline uint64_t reader_io_position();
-  inline uint64_t writer_io_position();
+  inline wuffs_base__io_buffer__struct* reader();  // Deprecated.
+  inline wuffs_base__io_buffer__struct* writer();  // Deprecated.
+  inline uint64_t reader_available() const;
+  inline uint64_t reader_io_position() const;
+  inline uint64_t writer_available() const;
+  inline uint64_t writer_io_position() const;
 #endif  // __cplusplus
 
 } wuffs_base__io_buffer;
@@ -1356,24 +1349,6 @@
   return ret;
 }
 
-static inline wuffs_base__io_reader  //
-wuffs_base__null_io_reader() {
-  wuffs_base__io_reader ret;
-  ret.private_impl.buf = NULL;
-  ret.private_impl.mark = NULL;
-  ret.private_impl.limit = NULL;
-  return ret;
-}
-
-static inline wuffs_base__io_writer  //
-wuffs_base__null_io_writer() {
-  wuffs_base__io_writer ret;
-  ret.private_impl.buf = NULL;
-  ret.private_impl.mark = NULL;
-  ret.private_impl.limit = NULL;
-  return ret;
-}
-
 // wuffs_base__io_buffer__compact moves any written but unread bytes to the
 // start of the buffer.
 static inline void  //
@@ -1390,31 +1365,23 @@
   buf->meta.ri = 0;
 }
 
-static inline wuffs_base__io_reader  //
-wuffs_base__io_buffer__reader(wuffs_base__io_buffer* buf) {
-  wuffs_base__io_reader ret;
-  ret.private_impl.buf = buf;
-  ret.private_impl.mark = NULL;
-  ret.private_impl.limit = NULL;
-  return ret;
-}
-
-static inline wuffs_base__io_writer  //
-wuffs_base__io_buffer__writer(wuffs_base__io_buffer* buf) {
-  wuffs_base__io_writer ret;
-  ret.private_impl.buf = buf;
-  ret.private_impl.mark = NULL;
-  ret.private_impl.limit = NULL;
-  return ret;
+static inline uint64_t  //
+wuffs_base__io_buffer__reader_available(const wuffs_base__io_buffer* buf) {
+  return buf ? buf->meta.wi - buf->meta.ri : 0;
 }
 
 static inline uint64_t  //
-wuffs_base__io_buffer__reader_io_position(wuffs_base__io_buffer* buf) {
+wuffs_base__io_buffer__reader_io_position(const wuffs_base__io_buffer* buf) {
   return buf ? wuffs_base__u64__sat_add(buf->meta.pos, buf->meta.ri) : 0;
 }
 
 static inline uint64_t  //
-wuffs_base__io_buffer__writer_io_position(wuffs_base__io_buffer* buf) {
+wuffs_base__io_buffer__writer_available(const wuffs_base__io_buffer* buf) {
+  return buf ? buf->data.len - buf->meta.wi : 0;
+}
+
+static inline uint64_t  //
+wuffs_base__io_buffer__writer_io_position(const wuffs_base__io_buffer* buf) {
   return buf ? wuffs_base__u64__sat_add(buf->meta.pos, buf->meta.wi) : 0;
 }
 
@@ -1425,23 +1392,33 @@
   wuffs_base__io_buffer__compact(this);
 }
 
-inline wuffs_base__io_reader  //
+inline wuffs_base__io_buffer*  //
 wuffs_base__io_buffer__struct::reader() {
-  return wuffs_base__io_buffer__reader(this);
+  return this;
 }
 
-inline wuffs_base__io_writer  //
+inline wuffs_base__io_buffer*  //
 wuffs_base__io_buffer__struct::writer() {
-  return wuffs_base__io_buffer__writer(this);
+  return this;
 }
 
 inline uint64_t  //
-wuffs_base__io_buffer__struct::reader_io_position() {
+wuffs_base__io_buffer__struct::reader_available() const {
+  return wuffs_base__io_buffer__reader_available(this);
+}
+
+inline uint64_t  //
+wuffs_base__io_buffer__struct::reader_io_position() const {
   return wuffs_base__io_buffer__reader_io_position(this);
 }
 
 inline uint64_t  //
-wuffs_base__io_buffer__struct::writer_io_position() {
+wuffs_base__io_buffer__struct::writer_available() const {
+  return wuffs_base__io_buffer__writer_available(this);
+}
+
+inline uint64_t  //
+wuffs_base__io_buffer__struct::writer_io_position() const {
   return wuffs_base__io_buffer__writer_io_position(this);
 }
 
@@ -1525,9 +1502,9 @@
 //  - bit        20 indicates big-endian/MSB-first (as opposed to little/LSB).
 //  - bit        19 indicates floating point (as opposed to integer).
 //  - bit        18 indicates palette-indexed. The number-of-planes (the next
-//                  field) will be zero, as the format is considered packed,
+//                  field) will be 0, as the format is considered interleaved,
 //                  but the 8-bit N-BGRA color data is stored in plane 3.
-//  - bits 17 .. 16 are the number of planes, minus 1. Zero means packed.
+//  - bits 17 .. 16 are the number of planes, minus 1. Zero means interleaved.
 //  - bits 15 .. 12 encodes the number of bits (depth) in the 3rd channel.
 //  - bits 11 ..  8 encodes the number of bits (depth) in the 2nd channel.
 //  - bits  7 ..  4 encodes the number of bits (depth) in the 1st channel.
@@ -1544,14 +1521,14 @@
 // channels: cyan, magenta, yellow, black).
 //
 // For direct formats with N > 1 channels, those channels can be laid out in
-// either 1 (packed) or N (planar) planes. For example, RGBA data is usually
-// packed, but YCbCr data is usually planar, due to chroma subsampling (for
-// details, see the wuffs_base__pixel_subsampling type).
+// either 1 (interleaved) or N (planar) planes. For example, RGBA data is
+// usually interleaved, but YCbCr data is usually planar, due to chroma
+// subsampling (for details, see the wuffs_base__pixel_subsampling type).
 //
 // For indexed formats, the palette (always 256 × 4 bytes) holds 8 bits per
 // channel non-alpha-premultiplied BGRA color data. There is only 1 plane (for
-// the index), as the format is considered packed. Plane 0 holds the per-pixel
-// indices. Plane 3 is re-purposed to hold the per-index colors.
+// the index), as the format is considered interleaved. Plane 0 holds the
+// per-pixel indices. Plane 3 is re-purposed to hold the per-index colors.
 //
 // The color field is encoded in 3 bits:
 //  - 0 means                   A (Alpha).
@@ -1565,8 +1542,8 @@
 //
 // In Wuffs, channels are given in memory order (also known as byte order),
 // regardless of endianness, since the C type for the pixel data is an array of
-// bytes, not an array of uint32_t. For example, packed BGRA with 8 bits per
-// channel means that the bytes in memory are always Blue, Green, Red then
+// bytes, not an array of uint32_t. For example, interleaved BGRA with 8 bits
+// per channel means that the bytes in memory are always Blue, Green, Red then
 // Alpha. On big-endian systems, that is the uint32_t 0xBBGGRRAA. On
 // little-endian, 0xAARRGGBB.
 //
@@ -1599,15 +1576,15 @@
 //
 // For example, wuffs_base__pixel_format 0x5510BBBB is a natural format for
 // decoding a PNG image - network byte order (also known as big-endian),
-// packed, non-premultiplied alpha - that happens to be 16-bit-depth truecolor
-// with alpha (RGBA). In memory order:
+// interleaved, non-premultiplied alpha - that happens to be 16-bit-depth
+// truecolor with alpha (RGBA). In memory order:
 //
 //  ptr+0  ptr+1  ptr+2  ptr+3  ptr+4  ptr+5  ptr+6  ptr+7
 //  Rhi    Rlo    Ghi    Glo    Bhi    Blo    Ahi    Alo
 //
 // For example, the value wuffs_base__pixel_format 0x40000565 means BGR with no
-// alpha or padding, 5/6/5 bits for blue/green/red, packed 2 bytes per pixel,
-// laid out LSB-first in memory order:
+// alpha or padding, 5/6/5 bits for blue/green/red, interleaved 2 bytes per
+// pixel, laid out LSB-first in memory order:
 //
 //  ptr+0...........  ptr+1...........
 //  MSB          LSB  MSB          LSB
@@ -1692,8 +1669,8 @@
   return f != 0;
 }
 
-// wuffs_base__pixel_format__bits_per_pixel returns, for packed pixel formats,
-// the number of bits per pixel. It returns 0 for planar pixel formats.
+// wuffs_base__pixel_format__bits_per_pixel returns the number of bits per
+// pixel for interleaved pixel formats, and returns 0 for planar pixel formats.
 static inline uint32_t  //
 wuffs_base__pixel_format__bits_per_pixel(wuffs_base__pixel_format f) {
   if (((f >> 16) & 0x03) != 0) {
@@ -1711,7 +1688,7 @@
 }
 
 static inline bool  //
-wuffs_base__pixel_format__is_packed(wuffs_base__pixel_format f) {
+wuffs_base__pixel_format__is_interleaved(wuffs_base__pixel_format f) {
   return ((f >> 16) & 0x03) == 0;
 }
 
@@ -1737,7 +1714,7 @@
 // plane p. For a depth of 8 bits (1 byte), the p'th plane's sample starts at
 // (planes[p].ptr + (j * planes[p].stride) + i).
 //
-// For packed pixel formats, the mapping is trivial: i = x and j = y. For
+// For interleaved pixel formats, the mapping is trivial: i = x and j = y. For
 // planar pixel formats, the mapping can differ due to chroma subsampling. For
 // example, consider a three plane YCbCr pixel format with 4:2:2 subsampling.
 // For the luma (Y) channel, there is one sample for every pixel, but for the
@@ -1828,13 +1805,13 @@
                   uint32_t width,
                   uint32_t height);
   inline void invalidate();
-  inline bool is_valid();
-  inline wuffs_base__pixel_format pixel_format();
-  inline wuffs_base__pixel_subsampling pixel_subsampling();
-  inline wuffs_base__rect_ie_u32 bounds();
-  inline uint32_t width();
-  inline uint32_t height();
-  inline uint64_t pixbuf_len();
+  inline bool is_valid() const;
+  inline wuffs_base__pixel_format pixel_format() const;
+  inline wuffs_base__pixel_subsampling pixel_subsampling() const;
+  inline wuffs_base__rect_ie_u32 bounds() const;
+  inline uint32_t width() const;
+  inline uint32_t height() const;
+  inline uint64_t pixbuf_len() const;
 #endif  // __cplusplus
 
 } wuffs_base__pixel_config;
@@ -1888,22 +1865,22 @@
 }
 
 static inline bool  //
-wuffs_base__pixel_config__is_valid(wuffs_base__pixel_config* c) {
+wuffs_base__pixel_config__is_valid(const wuffs_base__pixel_config* c) {
   return c && c->private_impl.pixfmt;
 }
 
 static inline wuffs_base__pixel_format  //
-wuffs_base__pixel_config__pixel_format(wuffs_base__pixel_config* c) {
+wuffs_base__pixel_config__pixel_format(const wuffs_base__pixel_config* c) {
   return c ? c->private_impl.pixfmt : 0;
 }
 
 static inline wuffs_base__pixel_subsampling  //
-wuffs_base__pixel_config__pixel_subsampling(wuffs_base__pixel_config* c) {
+wuffs_base__pixel_config__pixel_subsampling(const wuffs_base__pixel_config* c) {
   return c ? c->private_impl.pixsub : 0;
 }
 
 static inline wuffs_base__rect_ie_u32  //
-wuffs_base__pixel_config__bounds(wuffs_base__pixel_config* c) {
+wuffs_base__pixel_config__bounds(const wuffs_base__pixel_config* c) {
   if (c) {
     wuffs_base__rect_ie_u32 ret;
     ret.min_incl_x = 0;
@@ -1922,20 +1899,20 @@
 }
 
 static inline uint32_t  //
-wuffs_base__pixel_config__width(wuffs_base__pixel_config* c) {
+wuffs_base__pixel_config__width(const wuffs_base__pixel_config* c) {
   return c ? c->private_impl.width : 0;
 }
 
 static inline uint32_t  //
-wuffs_base__pixel_config__height(wuffs_base__pixel_config* c) {
+wuffs_base__pixel_config__height(const wuffs_base__pixel_config* c) {
   return c ? c->private_impl.height : 0;
 }
 
-// TODO: this is the right API for planar (not packed) pixbufs? Should it allow
-// decoding into a color model different from the format's intrinsic one? For
-// example, decoding a JPEG image straight to RGBA instead of to YCbCr?
+// TODO: this is the right API for planar (not interleaved) pixbufs? Should it
+// allow decoding into a color model different from the format's intrinsic one?
+// For example, decoding a JPEG image straight to RGBA instead of to YCbCr?
 static inline uint64_t  //
-wuffs_base__pixel_config__pixbuf_len(wuffs_base__pixel_config* c) {
+wuffs_base__pixel_config__pixbuf_len(const wuffs_base__pixel_config* c) {
   if (!c) {
     return 0;
   }
@@ -1984,37 +1961,37 @@
 }
 
 inline bool  //
-wuffs_base__pixel_config::is_valid() {
+wuffs_base__pixel_config::is_valid() const {
   return wuffs_base__pixel_config__is_valid(this);
 }
 
 inline wuffs_base__pixel_format  //
-wuffs_base__pixel_config::pixel_format() {
+wuffs_base__pixel_config::pixel_format() const {
   return wuffs_base__pixel_config__pixel_format(this);
 }
 
 inline wuffs_base__pixel_subsampling  //
-wuffs_base__pixel_config::pixel_subsampling() {
+wuffs_base__pixel_config::pixel_subsampling() const {
   return wuffs_base__pixel_config__pixel_subsampling(this);
 }
 
 inline wuffs_base__rect_ie_u32  //
-wuffs_base__pixel_config::bounds() {
+wuffs_base__pixel_config::bounds() const {
   return wuffs_base__pixel_config__bounds(this);
 }
 
 inline uint32_t  //
-wuffs_base__pixel_config::width() {
+wuffs_base__pixel_config::width() const {
   return wuffs_base__pixel_config__width(this);
 }
 
 inline uint32_t  //
-wuffs_base__pixel_config::height() {
+wuffs_base__pixel_config::height() const {
   return wuffs_base__pixel_config__height(this);
 }
 
 inline uint64_t  //
-wuffs_base__pixel_config::pixbuf_len() {
+wuffs_base__pixel_config::pixbuf_len() const {
   return wuffs_base__pixel_config__pixbuf_len(this);
 }
 
@@ -2040,9 +2017,9 @@
                   uint64_t first_frame_io_position,
                   bool first_frame_is_opaque);
   inline void invalidate();
-  inline bool is_valid();
-  inline uint64_t first_frame_io_position();
-  inline bool first_frame_is_opaque();
+  inline bool is_valid() const;
+  inline uint64_t first_frame_io_position() const;
+  inline bool first_frame_is_opaque() const;
 #endif  // __cplusplus
 
 } wuffs_base__image_config;
@@ -2099,17 +2076,19 @@
 }
 
 static inline bool  //
-wuffs_base__image_config__is_valid(wuffs_base__image_config* c) {
+wuffs_base__image_config__is_valid(const wuffs_base__image_config* c) {
   return c && wuffs_base__pixel_config__is_valid(&(c->pixcfg));
 }
 
 static inline uint64_t  //
-wuffs_base__image_config__first_frame_io_position(wuffs_base__image_config* c) {
+wuffs_base__image_config__first_frame_io_position(
+    const wuffs_base__image_config* c) {
   return c ? c->private_impl.first_frame_io_position : 0;
 }
 
 static inline bool  //
-wuffs_base__image_config__first_frame_is_opaque(wuffs_base__image_config* c) {
+wuffs_base__image_config__first_frame_is_opaque(
+    const wuffs_base__image_config* c) {
   return c ? c->private_impl.first_frame_is_opaque : false;
 }
 
@@ -2132,17 +2111,17 @@
 }
 
 inline bool  //
-wuffs_base__image_config::is_valid() {
+wuffs_base__image_config::is_valid() const {
   return wuffs_base__image_config__is_valid(this);
 }
 
 inline uint64_t  //
-wuffs_base__image_config::first_frame_io_position() {
+wuffs_base__image_config::first_frame_io_position() const {
   return wuffs_base__image_config__first_frame_io_position(this);
 }
 
 inline bool  //
-wuffs_base__image_config::first_frame_is_opaque() {
+wuffs_base__image_config::first_frame_is_opaque() const {
   return wuffs_base__image_config__first_frame_is_opaque(this);
 }
 
@@ -2198,6 +2177,7 @@
     uint64_t io_position;
     wuffs_base__animation_blend blend;
     wuffs_base__animation_disposal disposal;
+    wuffs_base__color_u32_argb_premul background_color;
   } private_impl;
 
 #ifdef __cplusplus
@@ -2206,15 +2186,17 @@
                      uint64_t index,
                      uint64_t io_position,
                      wuffs_base__animation_blend blend,
-                     wuffs_base__animation_disposal disposal);
-  inline wuffs_base__rect_ie_u32 bounds();
-  inline uint32_t width();
-  inline uint32_t height();
-  inline wuffs_base__flicks duration();
-  inline uint64_t index();
-  inline uint64_t io_position();
-  inline wuffs_base__animation_blend blend();
-  inline wuffs_base__animation_disposal disposal();
+                     wuffs_base__animation_disposal disposal,
+                     wuffs_base__color_u32_argb_premul background_color);
+  inline wuffs_base__rect_ie_u32 bounds() const;
+  inline uint32_t width() const;
+  inline uint32_t height() const;
+  inline wuffs_base__flicks duration() const;
+  inline uint64_t index() const;
+  inline uint64_t io_position() const;
+  inline wuffs_base__animation_blend blend() const;
+  inline wuffs_base__animation_disposal disposal() const;
+  inline wuffs_base__color_u32_argb_premul background_color() const;
 #endif  // __cplusplus
 
 } wuffs_base__frame_config;
@@ -2232,13 +2214,15 @@
 }
 
 static inline void  //
-wuffs_base__frame_config__update(wuffs_base__frame_config* c,
-                                 wuffs_base__rect_ie_u32 bounds,
-                                 wuffs_base__flicks duration,
-                                 uint64_t index,
-                                 uint64_t io_position,
-                                 wuffs_base__animation_blend blend,
-                                 wuffs_base__animation_disposal disposal) {
+wuffs_base__frame_config__update(
+    wuffs_base__frame_config* c,
+    wuffs_base__rect_ie_u32 bounds,
+    wuffs_base__flicks duration,
+    uint64_t index,
+    uint64_t io_position,
+    wuffs_base__animation_blend blend,
+    wuffs_base__animation_disposal disposal,
+    wuffs_base__color_u32_argb_premul background_color) {
   if (!c) {
     return;
   }
@@ -2249,10 +2233,11 @@
   c->private_impl.io_position = io_position;
   c->private_impl.blend = blend;
   c->private_impl.disposal = disposal;
+  c->private_impl.background_color = background_color;
 }
 
 static inline wuffs_base__rect_ie_u32  //
-wuffs_base__frame_config__bounds(wuffs_base__frame_config* c) {
+wuffs_base__frame_config__bounds(const wuffs_base__frame_config* c) {
   if (c) {
     return c->private_impl.bounds;
   }
@@ -2266,103 +2251,115 @@
 }
 
 static inline uint32_t  //
-wuffs_base__frame_config__width(wuffs_base__frame_config* c) {
+wuffs_base__frame_config__width(const wuffs_base__frame_config* c) {
   return c ? wuffs_base__rect_ie_u32__width(&c->private_impl.bounds) : 0;
 }
 
 static inline uint32_t  //
-wuffs_base__frame_config__height(wuffs_base__frame_config* c) {
+wuffs_base__frame_config__height(const wuffs_base__frame_config* c) {
   return c ? wuffs_base__rect_ie_u32__height(&c->private_impl.bounds) : 0;
 }
 
 // wuffs_base__frame_config__duration returns the amount of time to display
 // this frame. Zero means to display forever - a still (non-animated) image.
 static inline wuffs_base__flicks  //
-wuffs_base__frame_config__duration(wuffs_base__frame_config* c) {
+wuffs_base__frame_config__duration(const wuffs_base__frame_config* c) {
   return c ? c->private_impl.duration : 0;
 }
 
 // wuffs_base__frame_config__index returns the index of this frame. The first
 // frame in an image has index 0, the second frame has index 1, and so on.
 static inline uint64_t  //
-wuffs_base__frame_config__index(wuffs_base__frame_config* c) {
+wuffs_base__frame_config__index(const wuffs_base__frame_config* c) {
   return c ? c->private_impl.index : 0;
 }
 
 // wuffs_base__frame_config__io_position returns the I/O stream position before
 // the frame config.
 static inline uint64_t  //
-wuffs_base__frame_config__io_position(wuffs_base__frame_config* c) {
+wuffs_base__frame_config__io_position(const wuffs_base__frame_config* c) {
   return c ? c->private_impl.io_position : 0;
 }
 
 // wuffs_base__frame_config__blend returns, for an animated image, how to blend
 // the transparent pixels of this frame with the existing canvas.
 static inline wuffs_base__animation_blend  //
-wuffs_base__frame_config__blend(wuffs_base__frame_config* c) {
+wuffs_base__frame_config__blend(const wuffs_base__frame_config* c) {
   return c ? c->private_impl.blend : 0;
 }
 
 // wuffs_base__frame_config__disposal returns, for an animated image, how to
 // dispose of this frame after displaying it.
 static inline wuffs_base__animation_disposal  //
-wuffs_base__frame_config__disposal(wuffs_base__frame_config* c) {
+wuffs_base__frame_config__disposal(const wuffs_base__frame_config* c) {
   return c ? c->private_impl.disposal : 0;
 }
 
+static inline wuffs_base__color_u32_argb_premul  //
+wuffs_base__frame_config__background_color(const wuffs_base__frame_config* c) {
+  return c ? c->private_impl.background_color : 0;
+}
+
 #ifdef __cplusplus
 
 inline void  //
-wuffs_base__frame_config::update(wuffs_base__rect_ie_u32 bounds,
-                                 wuffs_base__flicks duration,
-                                 uint64_t index,
-                                 uint64_t io_position,
-                                 wuffs_base__animation_blend blend,
-                                 wuffs_base__animation_disposal disposal) {
+wuffs_base__frame_config::update(
+    wuffs_base__rect_ie_u32 bounds,
+    wuffs_base__flicks duration,
+    uint64_t index,
+    uint64_t io_position,
+    wuffs_base__animation_blend blend,
+    wuffs_base__animation_disposal disposal,
+    wuffs_base__color_u32_argb_premul background_color) {
   wuffs_base__frame_config__update(this, bounds, duration, index, io_position,
-                                   blend, disposal);
+                                   blend, disposal, background_color);
 }
 
 inline wuffs_base__rect_ie_u32  //
-wuffs_base__frame_config::bounds() {
+wuffs_base__frame_config::bounds() const {
   return wuffs_base__frame_config__bounds(this);
 }
 
 inline uint32_t  //
-wuffs_base__frame_config::width() {
+wuffs_base__frame_config::width() const {
   return wuffs_base__frame_config__width(this);
 }
 
 inline uint32_t  //
-wuffs_base__frame_config::height() {
+wuffs_base__frame_config::height() const {
   return wuffs_base__frame_config__height(this);
 }
 
 inline wuffs_base__flicks  //
-wuffs_base__frame_config::duration() {
+wuffs_base__frame_config::duration() const {
   return wuffs_base__frame_config__duration(this);
 }
 
 inline uint64_t  //
-wuffs_base__frame_config::index() {
+wuffs_base__frame_config::index() const {
   return wuffs_base__frame_config__index(this);
 }
 
 inline uint64_t  //
-wuffs_base__frame_config::io_position() {
+wuffs_base__frame_config::io_position() const {
   return wuffs_base__frame_config__io_position(this);
 }
 
 inline wuffs_base__animation_blend  //
-wuffs_base__frame_config::blend() {
+wuffs_base__frame_config::blend() const {
   return wuffs_base__frame_config__blend(this);
 }
 
 inline wuffs_base__animation_disposal  //
-wuffs_base__frame_config::disposal() {
+wuffs_base__frame_config::disposal() const {
   return wuffs_base__frame_config__disposal(this);
 }
 
+inline wuffs_base__color_u32_argb_premul  //
+wuffs_base__frame_config::background_color() const {
+  return wuffs_base__frame_config__background_color(this);
+}
+
 #endif  // __cplusplus
 
 // --------
@@ -2380,8 +2377,10 @@
 #ifdef __cplusplus
   inline wuffs_base__status set_from_slice(wuffs_base__pixel_config* pixcfg,
                                            wuffs_base__slice_u8 pixbuf_memory);
+  inline wuffs_base__status set_from_table(wuffs_base__pixel_config* pixcfg,
+                                           wuffs_base__table_u8 pixbuf_memory);
   inline wuffs_base__slice_u8 palette();
-  inline wuffs_base__pixel_format pixel_format();
+  inline wuffs_base__pixel_format pixel_format() const;
   inline wuffs_base__table_u8 plane(uint32_t p);
 #endif  // __cplusplus
 
@@ -2411,12 +2410,13 @@
   }
   if (wuffs_base__pixel_format__is_planar(pixcfg->private_impl.pixfmt)) {
     // TODO: support planar pixel formats, concious of pixel subsampling.
-    return wuffs_base__error__bad_argument;
+    return wuffs_base__error__unsupported_option;
   }
   uint32_t bits_per_pixel =
       wuffs_base__pixel_format__bits_per_pixel(pixcfg->private_impl.pixfmt);
   if ((bits_per_pixel == 0) || ((bits_per_pixel % 8) != 0)) {
-    return wuffs_base__error__bad_argument;
+    // TODO: support fraction-of-byte pixels, e.g. 1 bit per pixel?
+    return wuffs_base__error__unsupported_option;
   }
   uint64_t bytes_per_pixel = bits_per_pixel / 8;
 
@@ -2462,6 +2462,38 @@
   return NULL;
 }
 
+static inline wuffs_base__status  //
+wuffs_base__pixel_buffer__set_from_table(wuffs_base__pixel_buffer* b,
+                                         wuffs_base__pixel_config* pixcfg,
+                                         wuffs_base__table_u8 pixbuf_memory) {
+  if (!b) {
+    return wuffs_base__error__bad_receiver;
+  }
+  memset(b, 0, sizeof(*b));
+  if (!pixcfg ||
+      wuffs_base__pixel_format__is_planar(pixcfg->private_impl.pixfmt)) {
+    return wuffs_base__error__bad_argument;
+  }
+  uint32_t bits_per_pixel =
+      wuffs_base__pixel_format__bits_per_pixel(pixcfg->private_impl.pixfmt);
+  if ((bits_per_pixel == 0) || ((bits_per_pixel % 8) != 0)) {
+    // TODO: support fraction-of-byte pixels, e.g. 1 bit per pixel?
+    return wuffs_base__error__unsupported_option;
+  }
+  uint64_t bytes_per_pixel = bits_per_pixel / 8;
+
+  uint64_t width_in_bytes =
+      ((uint64_t)pixcfg->private_impl.width) * bytes_per_pixel;
+  if ((width_in_bytes > pixbuf_memory.width) ||
+      (pixcfg->private_impl.height > pixbuf_memory.height)) {
+    return wuffs_base__error__bad_argument;
+  }
+
+  b->pixcfg = *pixcfg;
+  b->private_impl.planes[0] = pixbuf_memory;
+  return NULL;
+}
+
 // wuffs_base__pixel_buffer__palette returns the palette color data. If
 // non-empty, it will have length 1024.
 static inline wuffs_base__slice_u8  //
@@ -2478,7 +2510,7 @@
 }
 
 static inline wuffs_base__pixel_format  //
-wuffs_base__pixel_buffer__pixel_format(wuffs_base__pixel_buffer* b) {
+wuffs_base__pixel_buffer__pixel_format(const wuffs_base__pixel_buffer* b) {
   if (b) {
     return b->pixcfg.private_impl.pixfmt;
   }
@@ -2507,13 +2539,19 @@
   return wuffs_base__pixel_buffer__set_from_slice(this, pixcfg, pixbuf_memory);
 }
 
+inline wuffs_base__status  //
+wuffs_base__pixel_buffer::set_from_table(wuffs_base__pixel_config* pixcfg,
+                                         wuffs_base__table_u8 pixbuf_memory) {
+  return wuffs_base__pixel_buffer__set_from_table(this, pixcfg, pixbuf_memory);
+}
+
 inline wuffs_base__slice_u8  //
 wuffs_base__pixel_buffer::palette() {
   return wuffs_base__pixel_buffer__palette(this);
 }
 
 inline wuffs_base__pixel_format  //
-wuffs_base__pixel_buffer::pixel_format() {
+wuffs_base__pixel_buffer::pixel_format() const {
   return wuffs_base__pixel_buffer__pixel_format(this);
 }
 
@@ -2555,20 +2593,18 @@
   } private_impl;
 
 #ifdef __cplusplus
-  inline void prepare(wuffs_base__pixel_format dst_format,
-                      wuffs_base__slice_u8 dst_palette,
-                      wuffs_base__pixel_format src_format,
-                      wuffs_base__slice_u8 src_palette);
-  inline uint64_t swizzle_packed(wuffs_base__slice_u8 dst,
-                                 wuffs_base__slice_u8 dst_palette,
-                                 wuffs_base__slice_u8 src);
+  inline wuffs_base__status prepare(wuffs_base__pixel_format dst_format,
+                                    wuffs_base__slice_u8 dst_palette,
+                                    wuffs_base__pixel_format src_format,
+                                    wuffs_base__slice_u8 src_palette);
+  inline uint64_t swizzle_interleaved(wuffs_base__slice_u8 dst,
+                                      wuffs_base__slice_u8 dst_palette,
+                                      wuffs_base__slice_u8 src) const;
 #endif  // __cplusplus
 
 } wuffs_base__pixel_swizzler;
 
-// TODO: should prepare (both the C and C++ methods) return a status?
-
-void  //
+wuffs_base__status  //
 wuffs_base__pixel_swizzler__prepare(wuffs_base__pixel_swizzler* p,
                                     wuffs_base__pixel_format dst_format,
                                     wuffs_base__slice_u8 dst_palette,
@@ -2576,28 +2612,30 @@
                                     wuffs_base__slice_u8 src_palette);
 
 uint64_t  //
-wuffs_base__pixel_swizzler__swizzle_packed(wuffs_base__pixel_swizzler* p,
-                                           wuffs_base__slice_u8 dst,
-                                           wuffs_base__slice_u8 dst_palette,
-                                           wuffs_base__slice_u8 src);
+wuffs_base__pixel_swizzler__swizzle_interleaved(
+    const wuffs_base__pixel_swizzler* p,
+    wuffs_base__slice_u8 dst,
+    wuffs_base__slice_u8 dst_palette,
+    wuffs_base__slice_u8 src);
 
 #ifdef __cplusplus
 
-inline void  //
+inline wuffs_base__status  //
 wuffs_base__pixel_swizzler::prepare(wuffs_base__pixel_format dst_format,
                                     wuffs_base__slice_u8 dst_palette,
                                     wuffs_base__pixel_format src_format,
                                     wuffs_base__slice_u8 src_palette) {
-  wuffs_base__pixel_swizzler__prepare(this, dst_format, dst_palette, src_format,
-                                      src_palette);
+  return wuffs_base__pixel_swizzler__prepare(this, dst_format, dst_palette,
+                                             src_format, src_palette);
 }
 
 uint64_t  //
-wuffs_base__pixel_swizzler::swizzle_packed(wuffs_base__slice_u8 dst,
-                                           wuffs_base__slice_u8 dst_palette,
-                                           wuffs_base__slice_u8 src) {
-  return wuffs_base__pixel_swizzler__swizzle_packed(this, dst, dst_palette,
-                                                    src);
+wuffs_base__pixel_swizzler::swizzle_interleaved(
+    wuffs_base__slice_u8 dst,
+    wuffs_base__slice_u8 dst_palette,
+    wuffs_base__slice_u8 src) const {
+  return wuffs_base__pixel_swizzler__swizzle_interleaved(this, dst, dst_palette,
+                                                         src);
 }
 
 #endif  // __cplusplus
@@ -2900,8 +2938,8 @@
 
 WUFFS_BASE__MAYBE_STATIC wuffs_base__status  //
 wuffs_deflate__decoder__decode_io_writer(wuffs_deflate__decoder* self,
-                                         wuffs_base__io_writer a_dst,
-                                         wuffs_base__io_reader a_src,
+                                         wuffs_base__io_buffer* a_dst,
+                                         wuffs_base__io_buffer* a_src,
                                          wuffs_base__slice_u8 a_workbuf);
 
 // ---------------- Struct Definitions
@@ -3019,8 +3057,8 @@
   }
 
   inline wuffs_base__status  //
-  decode_io_writer(wuffs_base__io_writer a_dst,
-                   wuffs_base__io_reader a_src,
+  decode_io_writer(wuffs_base__io_buffer* a_dst,
+                   wuffs_base__io_buffer* a_src,
                    wuffs_base__slice_u8 a_workbuf) {
     return wuffs_deflate__decoder__decode_io_writer(this, a_dst, a_src,
                                                     a_workbuf);
@@ -3091,8 +3129,8 @@
 
 WUFFS_BASE__MAYBE_STATIC wuffs_base__status  //
 wuffs_lzw__decoder__decode_io_writer(wuffs_lzw__decoder* self,
-                                     wuffs_base__io_writer a_dst,
-                                     wuffs_base__io_reader a_src,
+                                     wuffs_base__io_buffer* a_dst,
+                                     wuffs_base__io_buffer* a_src,
                                      wuffs_base__slice_u8 a_workbuf);
 
 WUFFS_BASE__MAYBE_STATIC wuffs_base__slice_u8  //
@@ -3189,8 +3227,8 @@
   }
 
   inline wuffs_base__status  //
-  decode_io_writer(wuffs_base__io_writer a_dst,
-                   wuffs_base__io_reader a_src,
+  decode_io_writer(wuffs_base__io_buffer* a_dst,
+                   wuffs_base__io_buffer* a_src,
                    wuffs_base__slice_u8 a_workbuf) {
     return wuffs_lzw__decoder__decode_io_writer(this, a_dst, a_src, a_workbuf);
   }
@@ -3225,9 +3263,11 @@
 
 extern const char* wuffs_gif__error__bad_block;
 extern const char* wuffs_gif__error__bad_extension_label;
+extern const char* wuffs_gif__error__bad_frame_size;
 extern const char* wuffs_gif__error__bad_graphic_control;
 extern const char* wuffs_gif__error__bad_header;
 extern const char* wuffs_gif__error__bad_literal_width;
+extern const char* wuffs_gif__error__bad_palette;
 
 // ---------------- Public Consts
 
@@ -3237,6 +3277,49 @@
     wuffs_gif__decoder_workbuf_len_max_incl_worst_case  //
         WUFFS_BASE__POTENTIALLY_UNUSED = 1;
 
+#define WUFFS_GIF__QUIRK_DELAY_NUM_DECODED_FRAMES 1041635328
+
+static const uint32_t                          //
+    wuffs_gif__quirk_delay_num_decoded_frames  //
+        WUFFS_BASE__POTENTIALLY_UNUSED = 1041635328;
+
+#define WUFFS_GIF__QUIRK_FIRST_FRAME_LOCAL_PALETTE_MEANS_BLACK_BACKGROUND \
+  1041635329
+
+static const uint32_t                                                  //
+    wuffs_gif__quirk_first_frame_local_palette_means_black_background  //
+        WUFFS_BASE__POTENTIALLY_UNUSED = 1041635329;
+
+#define WUFFS_GIF__QUIRK_HONOR_BACKGROUND_COLOR 1041635330
+
+static const uint32_t                        //
+    wuffs_gif__quirk_honor_background_color  //
+        WUFFS_BASE__POTENTIALLY_UNUSED = 1041635330;
+
+#define WUFFS_GIF__QUIRK_IGNORE_TOO_MUCH_PIXEL_DATA 1041635331
+
+static const uint32_t                            //
+    wuffs_gif__quirk_ignore_too_much_pixel_data  //
+        WUFFS_BASE__POTENTIALLY_UNUSED = 1041635331;
+
+#define WUFFS_GIF__QUIRK_IMAGE_BOUNDS_ARE_STRICT 1041635332
+
+static const uint32_t                         //
+    wuffs_gif__quirk_image_bounds_are_strict  //
+        WUFFS_BASE__POTENTIALLY_UNUSED = 1041635332;
+
+#define WUFFS_GIF__QUIRK_REJECT_EMPTY_FRAME 1041635333
+
+static const uint32_t                    //
+    wuffs_gif__quirk_reject_empty_frame  //
+        WUFFS_BASE__POTENTIALLY_UNUSED = 1041635333;
+
+#define WUFFS_GIF__QUIRK_REJECT_EMPTY_PALETTE 1041635334
+
+static const uint32_t                      //
+    wuffs_gif__quirk_reject_empty_palette  //
+        WUFFS_BASE__POTENTIALLY_UNUSED = 1041635334;
+
 // ---------------- Struct Declarations
 
 typedef struct wuffs_gif__decoder__struct wuffs_gif__decoder;
@@ -3260,10 +3343,30 @@
 
 // ---------------- Public Function Prototypes
 
+WUFFS_BASE__MAYBE_STATIC wuffs_base__empty_struct  //
+wuffs_gif__decoder__set_quirk_enabled(wuffs_gif__decoder* self,
+                                      uint32_t a_quirk,
+                                      bool a_enabled);
+
 WUFFS_BASE__MAYBE_STATIC wuffs_base__status  //
 wuffs_gif__decoder__decode_image_config(wuffs_gif__decoder* self,
                                         wuffs_base__image_config* a_dst,
-                                        wuffs_base__io_reader a_src);
+                                        wuffs_base__io_buffer* a_src);
+
+WUFFS_BASE__MAYBE_STATIC wuffs_base__empty_struct  //
+wuffs_gif__decoder__set_report_metadata(wuffs_gif__decoder* self,
+                                        uint32_t a_fourcc,
+                                        bool a_report);
+
+WUFFS_BASE__MAYBE_STATIC wuffs_base__status  //
+wuffs_gif__decoder__ack_metadata_chunk(wuffs_gif__decoder* self,
+                                       wuffs_base__io_buffer* a_src);
+
+WUFFS_BASE__MAYBE_STATIC uint32_t  //
+wuffs_gif__decoder__metadata_fourcc(const wuffs_gif__decoder* self);
+
+WUFFS_BASE__MAYBE_STATIC uint64_t  //
+wuffs_gif__decoder__metadata_chunk_length(const wuffs_gif__decoder* self);
 
 WUFFS_BASE__MAYBE_STATIC uint32_t  //
 wuffs_gif__decoder__num_animation_loops(const wuffs_gif__decoder* self);
@@ -3288,12 +3391,12 @@
 WUFFS_BASE__MAYBE_STATIC wuffs_base__status  //
 wuffs_gif__decoder__decode_frame_config(wuffs_gif__decoder* self,
                                         wuffs_base__frame_config* a_dst,
-                                        wuffs_base__io_reader a_src);
+                                        wuffs_base__io_buffer* a_src);
 
 WUFFS_BASE__MAYBE_STATIC wuffs_base__status  //
 wuffs_gif__decoder__decode_frame(wuffs_gif__decoder* self,
                                  wuffs_base__pixel_buffer* a_dst,
-                                 wuffs_base__io_reader a_src,
+                                 wuffs_base__io_buffer* a_src,
                                  wuffs_base__slice_u8 a_workbuf,
                                  wuffs_base__decode_frame_options* a_opts);
 
@@ -3323,14 +3426,29 @@
     uint32_t f_width;
     uint32_t f_height;
     uint8_t f_call_sequence;
+    bool f_ignore_metadata;
+    bool f_report_metadata_iccp;
+    bool f_report_metadata_xmp;
+    uint32_t f_metadata_fourcc_value;
+    uint64_t f_metadata_chunk_length_value;
+    uint64_t f_metadata_io_position;
+    bool f_quirk_enabled_delay_num_decoded_frames;
+    bool f_quirk_enabled_first_frame_local_palette_means_black_background;
+    bool f_quirk_enabled_honor_background_color;
+    bool f_quirk_enabled_ignore_too_much_pixel_data;
+    bool f_quirk_enabled_image_bounds_are_strict;
+    bool f_quirk_enabled_reject_empty_frame;
+    bool f_quirk_enabled_reject_empty_palette;
+    bool f_delayed_num_decoded_frames;
     bool f_end_of_data;
     bool f_restarted;
     bool f_previous_lzw_decode_ended_abruptly;
-    uint8_t f_which_palette;
+    bool f_has_global_palette;
     uint8_t f_interlace;
     bool f_seen_num_loops;
     uint32_t f_num_loops;
-    bool f_seen_graphic_control;
+    uint32_t f_background_color_u32_argb_premul;
+    uint32_t f_black_color_u32_argb_premul;
     bool f_gc_has_transparent_index;
     uint8_t f_gc_transparent_index;
     uint8_t f_gc_disposal;
@@ -3350,6 +3468,7 @@
     wuffs_base__pixel_swizzler f_swizzler;
 
     uint32_t p_decode_image_config[1];
+    uint32_t p_ack_metadata_chunk[1];
     uint32_t p_decode_frame_config[1];
     uint32_t p_skip_frame[1];
     uint32_t p_decode_frame[1];
@@ -3372,6 +3491,10 @@
     wuffs_lzw__decoder f_lzw;
 
     struct {
+      uint8_t v_blend;
+      uint32_t v_background_color;
+    } s_decode_frame_config[1];
+    struct {
       uint64_t scratch;
     } s_skip_frame[1];
     struct {
@@ -3380,6 +3503,7 @@
     } s_decode_header[1];
     struct {
       uint8_t v_flags;
+      uint8_t v_background_color_index;
       uint32_t v_num_palette_entries;
       uint32_t v_i;
       uint64_t scratch;
@@ -3389,8 +3513,10 @@
     } s_skip_blocks[1];
     struct {
       uint8_t v_block_size;
-      bool v_not_animexts;
-      bool v_not_netscape;
+      bool v_is_animexts;
+      bool v_is_netscape;
+      bool v_is_iccp;
+      bool v_is_xmp;
       uint64_t scratch;
     } s_decode_ae[1];
     struct {
@@ -3400,6 +3526,7 @@
       uint64_t scratch;
     } s_decode_id_part0[1];
     struct {
+      uint8_t v_which_palette;
       uint32_t v_num_palette_entries;
       uint32_t v_i;
       uint64_t scratch;
@@ -3444,12 +3571,37 @@
                                           initialize_flags);
   }
 
+  inline wuffs_base__empty_struct  //
+  set_quirk_enabled(uint32_t a_quirk, bool a_enabled) {
+    return wuffs_gif__decoder__set_quirk_enabled(this, a_quirk, a_enabled);
+  }
+
   inline wuffs_base__status  //
   decode_image_config(wuffs_base__image_config* a_dst,
-                      wuffs_base__io_reader a_src) {
+                      wuffs_base__io_buffer* a_src) {
     return wuffs_gif__decoder__decode_image_config(this, a_dst, a_src);
   }
 
+  inline wuffs_base__empty_struct  //
+  set_report_metadata(uint32_t a_fourcc, bool a_report) {
+    return wuffs_gif__decoder__set_report_metadata(this, a_fourcc, a_report);
+  }
+
+  inline wuffs_base__status  //
+  ack_metadata_chunk(wuffs_base__io_buffer* a_src) {
+    return wuffs_gif__decoder__ack_metadata_chunk(this, a_src);
+  }
+
+  inline uint32_t  //
+  metadata_fourcc() const {
+    return wuffs_gif__decoder__metadata_fourcc(this);
+  }
+
+  inline uint64_t  //
+  metadata_chunk_length() const {
+    return wuffs_gif__decoder__metadata_chunk_length(this);
+  }
+
   inline uint32_t  //
   num_animation_loops() const {
     return wuffs_gif__decoder__num_animation_loops(this);
@@ -3482,13 +3634,13 @@
 
   inline wuffs_base__status  //
   decode_frame_config(wuffs_base__frame_config* a_dst,
-                      wuffs_base__io_reader a_src) {
+                      wuffs_base__io_buffer* a_src) {
     return wuffs_gif__decoder__decode_frame_config(this, a_dst, a_src);
   }
 
   inline wuffs_base__status  //
   decode_frame(wuffs_base__pixel_buffer* a_dst,
-               wuffs_base__io_reader a_src,
+               wuffs_base__io_buffer* a_src,
                wuffs_base__slice_u8 a_workbuf,
                wuffs_base__decode_frame_options* a_opts) {
     return wuffs_gif__decoder__decode_frame(this, a_dst, a_src, a_workbuf,
@@ -3562,8 +3714,8 @@
 
 WUFFS_BASE__MAYBE_STATIC wuffs_base__status  //
 wuffs_gzip__decoder__decode_io_writer(wuffs_gzip__decoder* self,
-                                      wuffs_base__io_writer a_dst,
-                                      wuffs_base__io_reader a_src,
+                                      wuffs_base__io_buffer* a_dst,
+                                      wuffs_base__io_buffer* a_src,
                                       wuffs_base__slice_u8 a_workbuf);
 
 // ---------------- Struct Definitions
@@ -3650,8 +3802,8 @@
   }
 
   inline wuffs_base__status  //
-  decode_io_writer(wuffs_base__io_writer a_dst,
-                   wuffs_base__io_reader a_src,
+  decode_io_writer(wuffs_base__io_buffer* a_dst,
+                   wuffs_base__io_buffer* a_src,
                    wuffs_base__slice_u8 a_workbuf) {
     return wuffs_gzip__decoder__decode_io_writer(this, a_dst, a_src, a_workbuf);
   }
@@ -3723,8 +3875,8 @@
 
 WUFFS_BASE__MAYBE_STATIC wuffs_base__status  //
 wuffs_zlib__decoder__decode_io_writer(wuffs_zlib__decoder* self,
-                                      wuffs_base__io_writer a_dst,
-                                      wuffs_base__io_reader a_src,
+                                      wuffs_base__io_buffer* a_dst,
+                                      wuffs_base__io_buffer* a_src,
                                       wuffs_base__slice_u8 a_workbuf);
 
 // ---------------- Struct Definitions
@@ -3808,8 +3960,8 @@
   }
 
   inline wuffs_base__status  //
-  decode_io_writer(wuffs_base__io_writer a_dst,
-                   wuffs_base__io_reader a_src,
+  decode_io_writer(wuffs_base__io_buffer* a_dst,
+                   wuffs_base__io_buffer* a_src,
                    wuffs_base__slice_u8 a_workbuf) {
     return wuffs_zlib__decoder__decode_io_writer(this, a_dst, a_src, a_workbuf);
   }
@@ -4304,65 +4456,52 @@
 
 // ---------------- I/O
 
-static inline bool  //
-wuffs_base__io_buffer__is_valid(wuffs_base__io_buffer buf) {
-  return (buf.data.ptr || (buf.data.len == 0)) &&
-         (buf.data.len >= buf.meta.wi) && (buf.meta.wi >= buf.meta.ri);
-}
-
-// TODO: wuffs_base__io_reader__is_eof is no longer used by Wuffs per se, but
-// it might be handy to programs that use Wuffs. Either delete it, or promote
-// it to the public API.
+// "Null" as in "/dev/null", not as in "nullptr".
 //
-// If making this function public (i.e. moving it to base-header.h), it also
-// needs to allow NULL (i.e. implicit, callee-calculated) mark/limit.
+// TODO: ensure that this is zero-initialized.
+static wuffs_base__io_buffer wuffs_base__global__null_io_buffer;
 
-static inline bool  //
-wuffs_base__io_reader__is_eof(wuffs_base__io_reader o) {
-  wuffs_base__io_buffer* buf = o.private_impl.buf;
-  return buf && buf->meta.closed &&
-         (buf->data.ptr + buf->meta.wi == o.private_impl.limit);
+static inline wuffs_base__io_buffer*  //
+wuffs_base__null_io_reader() {
+  return &wuffs_base__global__null_io_buffer;
 }
 
-static inline bool  //
-wuffs_base__io_reader__is_valid(wuffs_base__io_reader o) {
-  wuffs_base__io_buffer* buf = o.private_impl.buf;
-  // Note: if making this function public (i.e. moving it to base-header.h), it
-  // also needs to allow NULL (i.e. implicit, callee-calculated) mark/limit.
-  return buf ? ((buf->data.ptr <= o.private_impl.mark) &&
-                (o.private_impl.mark <= o.private_impl.limit) &&
-                (o.private_impl.limit <= buf->data.ptr + buf->data.len))
-             : ((o.private_impl.mark == NULL) &&
-                (o.private_impl.limit == NULL));
+static inline wuffs_base__io_buffer*  //
+wuffs_base__null_io_writer() {
+  return &wuffs_base__global__null_io_buffer;
 }
 
-static inline bool  //
-wuffs_base__io_writer__is_valid(wuffs_base__io_writer o) {
-  wuffs_base__io_buffer* buf = o.private_impl.buf;
-  // Note: if making this function public (i.e. moving it to base-header.h), it
-  // also needs to allow NULL (i.e. implicit, callee-calculated) mark/limit.
-  return buf ? ((buf->data.ptr <= o.private_impl.mark) &&
-                (o.private_impl.mark <= o.private_impl.limit) &&
-                (o.private_impl.limit <= buf->data.ptr + buf->data.len))
-             : ((o.private_impl.mark == NULL) &&
-                (o.private_impl.limit == NULL));
+static inline uint64_t  //
+wuffs_base__io__count_since(uint64_t mark, uint64_t index) {
+  if (index >= mark) {
+    return index - mark;
+  }
+  return 0;
+}
+
+static inline wuffs_base__slice_u8  //
+wuffs_base__io__since(uint64_t mark, uint64_t index, uint8_t* ptr) {
+  if (index >= mark) {
+    return wuffs_base__make_slice_u8(ptr + mark, index - mark);
+  }
+  return wuffs_base__make_slice_u8(NULL, 0);
 }
 
 static inline uint32_t  //
 wuffs_base__io_writer__copy_n_from_history(uint8_t** ptr_iop_w,
-                                           uint8_t* io0_w,
                                            uint8_t* io1_w,
+                                           uint8_t* io2_w,
                                            uint32_t length,
                                            uint32_t distance) {
   if (!distance) {
     return 0;
   }
   uint8_t* p = *ptr_iop_w;
-  if ((size_t)(p - io0_w) < (size_t)(distance)) {
+  if ((size_t)(p - io1_w) < (size_t)(distance)) {
     return 0;
   }
   uint8_t* q = p - distance;
-  size_t n = (size_t)(io1_w - p);
+  size_t n = (size_t)(io2_w - p);
   if ((size_t)(length) > n) {
     length = (uint32_t)(n);
   } else {
@@ -4394,12 +4533,12 @@
 // wuffs_base__io_writer__copy_n_from_history function above, but has stronger
 // pre-conditions. The caller needs to prove that:
 //  - distance >  0
-//  - distance <= (*ptr_iop_w - io0_w)
-//  - length   <= (io1_w      - *ptr_iop_w)
+//  - distance <= (*ptr_iop_w - io1_w)
+//  - length   <= (io2_w      - *ptr_iop_w)
 static inline uint32_t  //
 wuffs_base__io_writer__copy_n_from_history_fast(uint8_t** ptr_iop_w,
-                                                uint8_t* io0_w,
                                                 uint8_t* io1_w,
+                                                uint8_t* io2_w,
                                                 uint32_t length,
                                                 uint32_t distance) {
   uint8_t* p = *ptr_iop_w;
@@ -4419,18 +4558,18 @@
 
 static inline uint32_t  //
 wuffs_base__io_writer__copy_n_from_reader(uint8_t** ptr_iop_w,
-                                          uint8_t* io1_w,
+                                          uint8_t* io2_w,
                                           uint32_t length,
                                           uint8_t** ptr_iop_r,
-                                          uint8_t* io1_r) {
+                                          uint8_t* io2_r) {
   uint8_t* iop_w = *ptr_iop_w;
   size_t n = length;
-  if (n > ((size_t)(io1_w - iop_w))) {
-    n = (size_t)(io1_w - iop_w);
+  if (n > ((size_t)(io2_w - iop_w))) {
+    n = (size_t)(io2_w - iop_w);
   }
   uint8_t* iop_r = *ptr_iop_r;
-  if (n > ((size_t)(io1_r - iop_r))) {
-    n = (size_t)(io1_r - iop_r);
+  if (n > ((size_t)(io2_r - iop_r))) {
+    n = (size_t)(io2_r - iop_r);
   }
   if (n > 0) {
     memmove(iop_w, iop_r, n);
@@ -4442,12 +4581,12 @@
 
 static inline uint64_t  //
 wuffs_base__io_writer__copy_from_slice(uint8_t** ptr_iop_w,
-                                       uint8_t* io1_w,
+                                       uint8_t* io2_w,
                                        wuffs_base__slice_u8 src) {
   uint8_t* iop_w = *ptr_iop_w;
   size_t n = src.len;
-  if (n > ((size_t)(io1_w - iop_w))) {
-    n = (size_t)(io1_w - iop_w);
+  if (n > ((size_t)(io2_w - iop_w))) {
+    n = (size_t)(io2_w - iop_w);
   }
   if (n > 0) {
     memmove(iop_w, src.ptr, n);
@@ -4458,7 +4597,7 @@
 
 static inline uint32_t  //
 wuffs_base__io_writer__copy_n_from_slice(uint8_t** ptr_iop_w,
-                                         uint8_t* io1_w,
+                                         uint8_t* io2_w,
                                          uint32_t length,
                                          wuffs_base__slice_u8 src) {
   uint8_t* iop_w = *ptr_iop_w;
@@ -4466,8 +4605,8 @@
   if (n > length) {
     n = length;
   }
-  if (n > ((size_t)(io1_w - iop_w))) {
-    n = (size_t)(io1_w - iop_w);
+  if (n > ((size_t)(io2_w - iop_w))) {
+    n = (size_t)(io2_w - iop_w);
   }
   if (n > 0) {
     memmove(iop_w, src.ptr, n);
@@ -4476,11 +4615,12 @@
   return (uint32_t)(n);
 }
 
-static inline wuffs_base__empty_struct  //
-wuffs_base__io_reader__set(wuffs_base__io_reader* o,
-                           wuffs_base__io_buffer* b,
+static inline wuffs_base__io_buffer*  //
+wuffs_base__io_reader__set(wuffs_base__io_buffer* b,
                            uint8_t** ptr_iop_r,
+                           uint8_t** ptr_io0_r,
                            uint8_t** ptr_io1_r,
+                           uint8_t** ptr_io2_r,
                            wuffs_base__slice_u8 data) {
   b->data = data;
   b->meta.wi = data.len;
@@ -4488,42 +4628,17 @@
   b->meta.pos = 0;
   b->meta.closed = false;
 
-  o->private_impl.buf = b;
-  o->private_impl.mark = data.ptr;
-  o->private_impl.limit = data.ptr + data.len;
   *ptr_iop_r = data.ptr;
-  *ptr_io1_r = data.ptr + data.len;
+  *ptr_io0_r = data.ptr;
+  *ptr_io1_r = data.ptr;
+  *ptr_io2_r = data.ptr + data.len;
 
-  wuffs_base__empty_struct ret;
-  ret.private_impl = 0;
-  return ret;
-}
-
-static inline wuffs_base__empty_struct  //
-wuffs_base__io_reader__set_limit(wuffs_base__io_reader* o,
-                                 uint8_t* iop_r,
-                                 uint64_t limit) {
-  if (o && (((size_t)(o->private_impl.limit - iop_r)) > limit)) {
-    o->private_impl.limit = iop_r + limit;
-  }
-
-  wuffs_base__empty_struct ret;
-  ret.private_impl = 0;
-  return ret;
-}
-
-static inline wuffs_base__empty_struct  //
-wuffs_base__io_reader__set_mark(wuffs_base__io_reader* o, uint8_t* mark) {
-  o->private_impl.mark = mark;
-
-  wuffs_base__empty_struct ret;
-  ret.private_impl = 0;
-  return ret;
+  return b;
 }
 
 static inline wuffs_base__slice_u8  //
-wuffs_base__io_reader__take(uint8_t** ptr_iop_r, uint8_t* io1_r, uint64_t n) {
-  if (n <= ((size_t)(io1_r - *ptr_iop_r))) {
+wuffs_base__io_reader__take(uint8_t** ptr_iop_r, uint8_t* io2_r, uint64_t n) {
+  if (n <= ((size_t)(io2_r - *ptr_iop_r))) {
     uint8_t* p = *ptr_iop_r;
     *ptr_iop_r += n;
     return wuffs_base__make_slice_u8(p, n);
@@ -4531,11 +4646,12 @@
   return wuffs_base__make_slice_u8(NULL, 0);
 }
 
-static inline wuffs_base__empty_struct  //
-wuffs_base__io_writer__set(wuffs_base__io_writer* o,
-                           wuffs_base__io_buffer* b,
+static inline wuffs_base__io_buffer*  //
+wuffs_base__io_writer__set(wuffs_base__io_buffer* b,
                            uint8_t** ptr_iop_w,
+                           uint8_t** ptr_io0_w,
                            uint8_t** ptr_io1_w,
+                           uint8_t** ptr_io2_w,
                            wuffs_base__slice_u8 data) {
   b->data = data;
   b->meta.wi = 0;
@@ -4543,24 +4659,12 @@
   b->meta.pos = 0;
   b->meta.closed = false;
 
-  o->private_impl.buf = b;
-  o->private_impl.mark = data.ptr;
-  o->private_impl.limit = data.ptr + data.len;
   *ptr_iop_w = data.ptr;
-  *ptr_io1_w = data.ptr + data.len;
+  *ptr_io0_w = data.ptr;
+  *ptr_io1_w = data.ptr;
+  *ptr_io2_w = data.ptr + data.len;
 
-  wuffs_base__empty_struct ret;
-  ret.private_impl = 0;
-  return ret;
-}
-
-static inline wuffs_base__empty_struct  //
-wuffs_base__io_writer__set_mark(wuffs_base__io_writer* o, uint8_t* mark) {
-  o->private_impl.mark = mark;
-
-  wuffs_base__empty_struct ret;
-  ret.private_impl = 0;
-  return ret;
+  return b;
 }
 
   // ---------------- I/O (Utility)
@@ -4618,8 +4722,10 @@
 };
 
 const char* wuffs_base__warning__end_of_data = "@base: end of data";
+const char* wuffs_base__warning__metadata_reported = "@base: metadata reported";
 const char* wuffs_base__suspension__short_read = "$base: short read";
 const char* wuffs_base__suspension__short_write = "$base: short write";
+const char* wuffs_base__error__bad_i_o_position = "#base: bad I/O position";
 const char* wuffs_base__error__bad_argument_length_too_short =
     "#base: bad argument (length too short)";
 const char* wuffs_base__error__bad_argument = "#base: bad argument";
@@ -4641,6 +4747,7 @@
 const char* wuffs_base__error__interleaved_coroutine_calls =
     "#base: interleaved coroutine calls";
 const char* wuffs_base__error__not_enough_data = "#base: not enough data";
+const char* wuffs_base__error__unsupported_option = "#base: unsupported option";
 const char* wuffs_base__error__too_much_data = "#base: too much data";
 
 // ---------------- Images
@@ -4658,6 +4765,61 @@
 }
 
 static uint64_t  //
+wuffs_base__pixel_swizzler__copy_3_1(wuffs_base__slice_u8 dst,
+                                     wuffs_base__slice_u8 dst_palette,
+                                     wuffs_base__slice_u8 src) {
+  if (dst_palette.len != 1024) {
+    return 0;
+  }
+  size_t dst_len3 = dst.len / 3;
+  size_t len = dst_len3 < src.len ? dst_len3 : src.len;
+  uint8_t* d = dst.ptr;
+  uint8_t* s = src.ptr;
+  size_t n = len;
+
+  // N is the loop unroll count.
+  const int N = 4;
+
+  // The comparison in the while condition is ">", not ">=", because with ">=",
+  // the last 4-byte store could write past the end of the dst slice.
+  //
+  // Each 4-byte store writes one too many bytes, but a subsequent store will
+  // overwrite that with the correct byte. There is always another store,
+  // whether a 4-byte store in this loop or a 1-byte store in the next loop.
+  while (n > N) {
+    wuffs_base__store_u32le(
+        d + (0 * 3),
+        wuffs_base__load_u32le(dst_palette.ptr + ((uint32_t)(s[0]) * 4)));
+    wuffs_base__store_u32le(
+        d + (1 * 3),
+        wuffs_base__load_u32le(dst_palette.ptr + ((uint32_t)(s[1]) * 4)));
+    wuffs_base__store_u32le(
+        d + (2 * 3),
+        wuffs_base__load_u32le(dst_palette.ptr + ((uint32_t)(s[2]) * 4)));
+    wuffs_base__store_u32le(
+        d + (3 * 3),
+        wuffs_base__load_u32le(dst_palette.ptr + ((uint32_t)(s[3]) * 4)));
+
+    s += 1 * N;
+    d += 3 * N;
+    n -= (size_t)(1 * N);
+  }
+
+  while (n >= 1) {
+    uint32_t color =
+        wuffs_base__load_u32le(dst_palette.ptr + ((uint32_t)(s[0]) * 4));
+    d[0] = (uint8_t)(color >> 0);
+    d[1] = (uint8_t)(color >> 8);
+    d[2] = (uint8_t)(color >> 16);
+
+    s += 1 * 1;
+    d += 3 * 1;
+    n -= (size_t)(1 * 1);
+  }
+
+  return len;
+}
+static uint64_t  //
 wuffs_base__pixel_swizzler__copy_4_1(wuffs_base__slice_u8 dst,
                                      wuffs_base__slice_u8 dst_palette,
                                      wuffs_base__slice_u8 src) {
@@ -4668,8 +4830,9 @@
   size_t len = dst_len4 < src.len ? dst_len4 : src.len;
   uint8_t* d = dst.ptr;
   uint8_t* s = src.ptr;
-
   size_t n = len;
+
+  // N is the loop unroll count.
   const int N = 4;
 
   while (n >= N) {
@@ -4727,14 +4890,14 @@
   return len4 * 4;
 }
 
-void  //
+wuffs_base__status  //
 wuffs_base__pixel_swizzler__prepare(wuffs_base__pixel_swizzler* p,
                                     wuffs_base__pixel_format dst_format,
                                     wuffs_base__slice_u8 dst_palette,
                                     wuffs_base__pixel_format src_format,
                                     wuffs_base__slice_u8 src_palette) {
   if (!p) {
-    return;
+    return wuffs_base__error__bad_receiver;
   }
 
   // TODO: support many more formats.
@@ -4754,6 +4917,13 @@
           }
           func = wuffs_base__pixel_swizzler__copy_1_1;
           break;
+        case WUFFS_BASE__PIXEL_FORMAT__BGR:
+          if (wuffs_base__slice_u8__copy_from_slice(dst_palette, src_palette) !=
+              1024) {
+            break;
+          }
+          func = wuffs_base__pixel_swizzler__copy_3_1;
+          break;
         case WUFFS_BASE__PIXEL_FORMAT__BGRA_NONPREMUL:
         case WUFFS_BASE__PIXEL_FORMAT__BGRA_PREMUL:
         case WUFFS_BASE__PIXEL_FORMAT__BGRA_BINARY:
@@ -4763,6 +4933,13 @@
           }
           func = wuffs_base__pixel_swizzler__copy_4_1;
           break;
+        case WUFFS_BASE__PIXEL_FORMAT__RGB:
+          if (wuffs_base__pixel_swizzler__swap_rgbx_bgrx(dst_palette,
+                                                         src_palette) != 1024) {
+            break;
+          }
+          func = wuffs_base__pixel_swizzler__copy_3_1;
+          break;
         case WUFFS_BASE__PIXEL_FORMAT__RGBA_NONPREMUL:
         case WUFFS_BASE__PIXEL_FORMAT__RGBA_PREMUL:
         case WUFFS_BASE__PIXEL_FORMAT__RGBA_BINARY:
@@ -4782,13 +4959,15 @@
   }
 
   p->private_impl.func = func;
+  return func ? NULL : wuffs_base__error__unsupported_option;
 }
 
 uint64_t  //
-wuffs_base__pixel_swizzler__swizzle_packed(wuffs_base__pixel_swizzler* p,
-                                           wuffs_base__slice_u8 dst,
-                                           wuffs_base__slice_u8 dst_palette,
-                                           wuffs_base__slice_u8 src) {
+wuffs_base__pixel_swizzler__swizzle_interleaved(
+    const wuffs_base__pixel_swizzler* p,
+    wuffs_base__slice_u8 dst,
+    wuffs_base__slice_u8 dst_palette,
+    wuffs_base__slice_u8 src) {
   if (p && p->private_impl.func) {
     return (*(p->private_impl.func))(dst, dst_palette, src);
   }
@@ -6091,20 +6270,20 @@
 
 static wuffs_base__status  //
 wuffs_deflate__decoder__decode_blocks(wuffs_deflate__decoder* self,
-                                      wuffs_base__io_writer a_dst,
-                                      wuffs_base__io_reader a_src);
+                                      wuffs_base__io_buffer* a_dst,
+                                      wuffs_base__io_buffer* a_src);
 
 static wuffs_base__status  //
 wuffs_deflate__decoder__decode_uncompressed(wuffs_deflate__decoder* self,
-                                            wuffs_base__io_writer a_dst,
-                                            wuffs_base__io_reader a_src);
+                                            wuffs_base__io_buffer* a_dst,
+                                            wuffs_base__io_buffer* a_src);
 
 static wuffs_base__status  //
 wuffs_deflate__decoder__init_fixed_huffman(wuffs_deflate__decoder* self);
 
 static wuffs_base__status  //
 wuffs_deflate__decoder__init_dynamic_huffman(wuffs_deflate__decoder* self,
-                                             wuffs_base__io_reader a_src);
+                                             wuffs_base__io_buffer* a_src);
 
 static wuffs_base__status  //
 wuffs_deflate__decoder__init_huff(wuffs_deflate__decoder* self,
@@ -6115,13 +6294,13 @@
 
 static wuffs_base__status  //
 wuffs_deflate__decoder__decode_huffman_fast(wuffs_deflate__decoder* self,
-                                            wuffs_base__io_writer a_dst,
-                                            wuffs_base__io_reader a_src);
+                                            wuffs_base__io_buffer* a_dst,
+                                            wuffs_base__io_buffer* a_src);
 
 static wuffs_base__status  //
 wuffs_deflate__decoder__decode_huffman_slow(wuffs_deflate__decoder* self,
-                                            wuffs_base__io_writer a_dst,
-                                            wuffs_base__io_reader a_src);
+                                            wuffs_base__io_buffer* a_dst,
+                                            wuffs_base__io_buffer* a_src);
 
 // ---------------- Initializer Implementations
 
@@ -6196,8 +6375,8 @@
 
 WUFFS_BASE__MAYBE_STATIC wuffs_base__status  //
 wuffs_deflate__decoder__decode_io_writer(wuffs_deflate__decoder* self,
-                                         wuffs_base__io_writer a_dst,
-                                         wuffs_base__io_reader a_src,
+                                         wuffs_base__io_buffer* a_dst,
+                                         wuffs_base__io_buffer* a_src,
                                          wuffs_base__slice_u8 a_workbuf) {
   if (!self) {
     return wuffs_base__error__bad_receiver;
@@ -6207,6 +6386,10 @@
                ? wuffs_base__error__disabled_by_previous_error
                : wuffs_base__error__initialize_not_called;
   }
+  if (!a_dst || !a_src) {
+    self->private_impl.magic = WUFFS_BASE__DISABLED;
+    return wuffs_base__error__bad_argument;
+  }
   if ((self->private_impl.active_coroutine != 0) &&
       (self->private_impl.active_coroutine != 1)) {
     self->private_impl.magic = WUFFS_BASE__DISABLED;
@@ -6215,6 +6398,7 @@
   self->private_impl.active_coroutine = 0;
   wuffs_base__status status = NULL;
 
+  uint64_t v_mark = 0;
   wuffs_base__status v_status = NULL;
   wuffs_base__slice_u8 v_written = {0};
   uint64_t v_n_copied = 0;
@@ -6223,19 +6407,15 @@
   uint8_t* iop_a_dst = NULL;
   uint8_t* io0_a_dst WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
   uint8_t* io1_a_dst WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
-  if (a_dst.private_impl.buf) {
-    iop_a_dst =
-        a_dst.private_impl.buf->data.ptr + a_dst.private_impl.buf->meta.wi;
-    if (!a_dst.private_impl.mark) {
-      a_dst.private_impl.mark = iop_a_dst;
-      a_dst.private_impl.limit =
-          a_dst.private_impl.buf->data.ptr + a_dst.private_impl.buf->data.len;
+  uint8_t* io2_a_dst WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
+  if (a_dst) {
+    io0_a_dst = a_dst->data.ptr;
+    io1_a_dst = io0_a_dst + a_dst->meta.wi;
+    iop_a_dst = io1_a_dst;
+    io2_a_dst = io0_a_dst + a_dst->data.len;
+    if (a_dst->meta.closed) {
+      io2_a_dst = iop_a_dst;
     }
-    if (a_dst.private_impl.buf->meta.closed) {
-      a_dst.private_impl.limit = iop_a_dst;
-    }
-    io0_a_dst = a_dst.private_impl.mark;
-    io1_a_dst = a_dst.private_impl.limit;
   }
 
   uint32_t coro_susp_point = self->private_impl.p_decode_io_writer[0];
@@ -6245,17 +6425,15 @@
     WUFFS_BASE__COROUTINE_SUSPENSION_POINT_0;
 
     while (true) {
-      wuffs_base__io_writer__set_mark(&a_dst, iop_a_dst);
+      v_mark = ((uint64_t)(iop_a_dst - io0_a_dst));
       {
-        if (a_dst.private_impl.buf) {
-          a_dst.private_impl.buf->meta.wi =
-              ((size_t)(iop_a_dst - a_dst.private_impl.buf->data.ptr));
+        if (a_dst) {
+          a_dst->meta.wi = ((size_t)(iop_a_dst - a_dst->data.ptr));
         }
         wuffs_base__status t_0 =
             wuffs_deflate__decoder__decode_blocks(self, a_dst, a_src);
-        if (a_dst.private_impl.buf) {
-          iop_a_dst = a_dst.private_impl.buf->data.ptr +
-                      a_dst.private_impl.buf->meta.wi;
+        if (a_dst) {
+          iop_a_dst = a_dst->data.ptr + a_dst->meta.wi;
         }
         v_status = t_0;
       }
@@ -6269,9 +6447,8 @@
         }
         goto ok;
       }
-      v_written = wuffs_base__make_slice_u8(
-          a_dst.private_impl.mark,
-          (size_t)(iop_a_dst - a_dst.private_impl.mark));
+      v_written = wuffs_base__io__since(
+          v_mark, ((uint64_t)(iop_a_dst - io0_a_dst)), io0_a_dst);
       if (((uint64_t)(v_written.len)) >= 32768) {
         v_written = wuffs_base__slice_u8__suffix(v_written, 32768);
         wuffs_base__slice_u8__copy_from_slice(
@@ -6313,14 +6490,15 @@
 
   goto suspend;
 suspend:
-  self->private_impl.p_decode_io_writer[0] = coro_susp_point;
-  self->private_impl.active_coroutine = 1;
+  self->private_impl.p_decode_io_writer[0] =
+      wuffs_base__status__is_suspension(status) ? coro_susp_point : 0;
+  self->private_impl.active_coroutine =
+      wuffs_base__status__is_suspension(status) ? 1 : 0;
 
   goto exit;
 exit:
-  if (a_dst.private_impl.buf) {
-    a_dst.private_impl.buf->meta.wi =
-        ((size_t)(iop_a_dst - a_dst.private_impl.buf->data.ptr));
+  if (a_dst) {
+    a_dst->meta.wi = ((size_t)(iop_a_dst - a_dst->data.ptr));
   }
 
   if (wuffs_base__status__is_error(status)) {
@@ -6333,8 +6511,8 @@
 
 static wuffs_base__status  //
 wuffs_deflate__decoder__decode_blocks(wuffs_deflate__decoder* self,
-                                      wuffs_base__io_writer a_dst,
-                                      wuffs_base__io_reader a_src) {
+                                      wuffs_base__io_buffer* a_dst,
+                                      wuffs_base__io_buffer* a_src) {
   wuffs_base__status status = NULL;
 
   uint32_t v_final = 0;
@@ -6345,16 +6523,12 @@
   uint8_t* iop_a_src = NULL;
   uint8_t* io0_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
   uint8_t* io1_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
-  if (a_src.private_impl.buf) {
-    iop_a_src =
-        a_src.private_impl.buf->data.ptr + a_src.private_impl.buf->meta.ri;
-    if (!a_src.private_impl.mark) {
-      a_src.private_impl.mark = iop_a_src;
-      a_src.private_impl.limit =
-          a_src.private_impl.buf->data.ptr + a_src.private_impl.buf->meta.wi;
-    }
-    io0_a_src = a_src.private_impl.mark;
-    io1_a_src = a_src.private_impl.limit;
+  uint8_t* io2_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
+  if (a_src) {
+    io0_a_src = a_src->data.ptr;
+    io1_a_src = io0_a_src + a_src->meta.ri;
+    iop_a_src = io1_a_src;
+    io2_a_src = io0_a_src + a_src->meta.wi;
   }
 
   uint32_t coro_susp_point = self->private_impl.p_decode_blocks[0];
@@ -6369,7 +6543,7 @@
       while (self->private_impl.f_n_bits < 3) {
         {
           WUFFS_BASE__COROUTINE_SUSPENSION_POINT(1);
-          if (WUFFS_BASE__UNLIKELY(iop_a_src == io1_a_src)) {
+          if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
             status = wuffs_base__suspension__short_read;
             goto suspend;
           }
@@ -6384,16 +6558,14 @@
       self->private_impl.f_bits >>= 3;
       self->private_impl.f_n_bits -= 3;
       if (v_type == 0) {
-        if (a_src.private_impl.buf) {
-          a_src.private_impl.buf->meta.ri =
-              ((size_t)(iop_a_src - a_src.private_impl.buf->data.ptr));
+        if (a_src) {
+          a_src->meta.ri = ((size_t)(iop_a_src - a_src->data.ptr));
         }
         WUFFS_BASE__COROUTINE_SUSPENSION_POINT(2);
         status =
             wuffs_deflate__decoder__decode_uncompressed(self, a_dst, a_src);
-        if (a_src.private_impl.buf) {
-          iop_a_src = a_src.private_impl.buf->data.ptr +
-                      a_src.private_impl.buf->meta.ri;
+        if (a_src) {
+          iop_a_src = a_src->data.ptr + a_src->meta.ri;
         }
         if (status) {
           goto suspend;
@@ -6412,15 +6584,13 @@
           goto ok;
         }
       } else if (v_type == 2) {
-        if (a_src.private_impl.buf) {
-          a_src.private_impl.buf->meta.ri =
-              ((size_t)(iop_a_src - a_src.private_impl.buf->data.ptr));
+        if (a_src) {
+          a_src->meta.ri = ((size_t)(iop_a_src - a_src->data.ptr));
         }
         WUFFS_BASE__COROUTINE_SUSPENSION_POINT(3);
         status = wuffs_deflate__decoder__init_dynamic_huffman(self, a_src);
-        if (a_src.private_impl.buf) {
-          iop_a_src = a_src.private_impl.buf->data.ptr +
-                      a_src.private_impl.buf->meta.ri;
+        if (a_src) {
+          iop_a_src = a_src->data.ptr + a_src->meta.ri;
         }
         if (status) {
           goto suspend;
@@ -6431,15 +6601,13 @@
       }
       self->private_impl.f_end_of_block = false;
       while (true) {
-        if (a_src.private_impl.buf) {
-          a_src.private_impl.buf->meta.ri =
-              ((size_t)(iop_a_src - a_src.private_impl.buf->data.ptr));
+        if (a_src) {
+          a_src->meta.ri = ((size_t)(iop_a_src - a_src->data.ptr));
         }
         v_status =
             wuffs_deflate__decoder__decode_huffman_fast(self, a_dst, a_src);
-        if (a_src.private_impl.buf) {
-          iop_a_src = a_src.private_impl.buf->data.ptr +
-                      a_src.private_impl.buf->meta.ri;
+        if (a_src) {
+          iop_a_src = a_src->data.ptr + a_src->meta.ri;
         }
         if (wuffs_base__status__is_error(v_status)) {
           status = v_status;
@@ -6448,16 +6616,14 @@
         if (self->private_impl.f_end_of_block) {
           goto label_0_continue;
         }
-        if (a_src.private_impl.buf) {
-          a_src.private_impl.buf->meta.ri =
-              ((size_t)(iop_a_src - a_src.private_impl.buf->data.ptr));
+        if (a_src) {
+          a_src->meta.ri = ((size_t)(iop_a_src - a_src->data.ptr));
         }
         WUFFS_BASE__COROUTINE_SUSPENSION_POINT(4);
         status =
             wuffs_deflate__decoder__decode_huffman_slow(self, a_dst, a_src);
-        if (a_src.private_impl.buf) {
-          iop_a_src = a_src.private_impl.buf->data.ptr +
-                      a_src.private_impl.buf->meta.ri;
+        if (a_src) {
+          iop_a_src = a_src->data.ptr + a_src->meta.ri;
         }
         if (status) {
           goto suspend;
@@ -6476,14 +6642,14 @@
 
   goto suspend;
 suspend:
-  self->private_impl.p_decode_blocks[0] = coro_susp_point;
+  self->private_impl.p_decode_blocks[0] =
+      wuffs_base__status__is_suspension(status) ? coro_susp_point : 0;
   self->private_data.s_decode_blocks[0].v_final = v_final;
 
   goto exit;
 exit:
-  if (a_src.private_impl.buf) {
-    a_src.private_impl.buf->meta.ri =
-        ((size_t)(iop_a_src - a_src.private_impl.buf->data.ptr));
+  if (a_src) {
+    a_src->meta.ri = ((size_t)(iop_a_src - a_src->data.ptr));
   }
 
   return status;
@@ -6493,8 +6659,8 @@
 
 static wuffs_base__status  //
 wuffs_deflate__decoder__decode_uncompressed(wuffs_deflate__decoder* self,
-                                            wuffs_base__io_writer a_dst,
-                                            wuffs_base__io_reader a_src) {
+                                            wuffs_base__io_buffer* a_dst,
+                                            wuffs_base__io_buffer* a_src) {
   wuffs_base__status status = NULL;
 
   uint32_t v_length = 0;
@@ -6503,33 +6669,25 @@
   uint8_t* iop_a_dst = NULL;
   uint8_t* io0_a_dst WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
   uint8_t* io1_a_dst WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
-  if (a_dst.private_impl.buf) {
-    iop_a_dst =
-        a_dst.private_impl.buf->data.ptr + a_dst.private_impl.buf->meta.wi;
-    if (!a_dst.private_impl.mark) {
-      a_dst.private_impl.mark = iop_a_dst;
-      a_dst.private_impl.limit =
-          a_dst.private_impl.buf->data.ptr + a_dst.private_impl.buf->data.len;
+  uint8_t* io2_a_dst WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
+  if (a_dst) {
+    io0_a_dst = a_dst->data.ptr;
+    io1_a_dst = io0_a_dst + a_dst->meta.wi;
+    iop_a_dst = io1_a_dst;
+    io2_a_dst = io0_a_dst + a_dst->data.len;
+    if (a_dst->meta.closed) {
+      io2_a_dst = iop_a_dst;
     }
-    if (a_dst.private_impl.buf->meta.closed) {
-      a_dst.private_impl.limit = iop_a_dst;
-    }
-    io0_a_dst = a_dst.private_impl.mark;
-    io1_a_dst = a_dst.private_impl.limit;
   }
   uint8_t* iop_a_src = NULL;
   uint8_t* io0_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
   uint8_t* io1_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
-  if (a_src.private_impl.buf) {
-    iop_a_src =
-        a_src.private_impl.buf->data.ptr + a_src.private_impl.buf->meta.ri;
-    if (!a_src.private_impl.mark) {
-      a_src.private_impl.mark = iop_a_src;
-      a_src.private_impl.limit =
-          a_src.private_impl.buf->data.ptr + a_src.private_impl.buf->meta.wi;
-    }
-    io0_a_src = a_src.private_impl.mark;
-    io1_a_src = a_src.private_impl.limit;
+  uint8_t* io2_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
+  if (a_src) {
+    io0_a_src = a_src->data.ptr;
+    io1_a_src = io0_a_src + a_src->meta.ri;
+    iop_a_src = io1_a_src;
+    io2_a_src = io0_a_src + a_src->meta.wi;
   }
 
   uint32_t coro_susp_point = self->private_impl.p_decode_uncompressed[0];
@@ -6550,14 +6708,14 @@
     {
       WUFFS_BASE__COROUTINE_SUSPENSION_POINT(1);
       uint32_t t_0;
-      if (WUFFS_BASE__LIKELY(io1_a_src - iop_a_src >= 4)) {
+      if (WUFFS_BASE__LIKELY(io2_a_src - iop_a_src >= 4)) {
         t_0 = wuffs_base__load_u32le(iop_a_src);
         iop_a_src += 4;
       } else {
         self->private_data.s_decode_uncompressed[0].scratch = 0;
         WUFFS_BASE__COROUTINE_SUSPENSION_POINT(2);
         while (true) {
-          if (WUFFS_BASE__UNLIKELY(iop_a_src == io1_a_src)) {
+          if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
             status = wuffs_base__suspension__short_read;
             goto suspend;
           }
@@ -6584,13 +6742,13 @@
     v_length = ((v_length)&0xFFFF);
     while (true) {
       v_n_copied = wuffs_base__io_writer__copy_n_from_reader(
-          &iop_a_dst, io1_a_dst, v_length, &iop_a_src, io1_a_src);
+          &iop_a_dst, io2_a_dst, v_length, &iop_a_src, io2_a_src);
       if (v_length <= v_n_copied) {
         status = NULL;
         goto ok;
       }
       v_length -= v_n_copied;
-      if (((uint64_t)(io1_a_dst - iop_a_dst)) == 0) {
+      if (((uint64_t)(io2_a_dst - iop_a_dst)) == 0) {
         status = wuffs_base__suspension__short_write;
         WUFFS_BASE__COROUTINE_SUSPENSION_POINT_MAYBE_SUSPEND(3);
       } else {
@@ -6607,18 +6765,17 @@
 
   goto suspend;
 suspend:
-  self->private_impl.p_decode_uncompressed[0] = coro_susp_point;
+  self->private_impl.p_decode_uncompressed[0] =
+      wuffs_base__status__is_suspension(status) ? coro_susp_point : 0;
   self->private_data.s_decode_uncompressed[0].v_length = v_length;
 
   goto exit;
 exit:
-  if (a_dst.private_impl.buf) {
-    a_dst.private_impl.buf->meta.wi =
-        ((size_t)(iop_a_dst - a_dst.private_impl.buf->data.ptr));
+  if (a_dst) {
+    a_dst->meta.wi = ((size_t)(iop_a_dst - a_dst->data.ptr));
   }
-  if (a_src.private_impl.buf) {
-    a_src.private_impl.buf->meta.ri =
-        ((size_t)(iop_a_src - a_src.private_impl.buf->data.ptr));
+  if (a_src) {
+    a_src->meta.ri = ((size_t)(iop_a_src - a_src->data.ptr));
   }
 
   return status;
@@ -6666,7 +6823,7 @@
 
 static wuffs_base__status  //
 wuffs_deflate__decoder__init_dynamic_huffman(wuffs_deflate__decoder* self,
-                                             wuffs_base__io_reader a_src) {
+                                             wuffs_base__io_buffer* a_src) {
   wuffs_base__status status = NULL;
 
   uint32_t v_bits = 0;
@@ -6690,16 +6847,12 @@
   uint8_t* iop_a_src = NULL;
   uint8_t* io0_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
   uint8_t* io1_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
-  if (a_src.private_impl.buf) {
-    iop_a_src =
-        a_src.private_impl.buf->data.ptr + a_src.private_impl.buf->meta.ri;
-    if (!a_src.private_impl.mark) {
-      a_src.private_impl.mark = iop_a_src;
-      a_src.private_impl.limit =
-          a_src.private_impl.buf->data.ptr + a_src.private_impl.buf->meta.wi;
-    }
-    io0_a_src = a_src.private_impl.mark;
-    io1_a_src = a_src.private_impl.limit;
+  uint8_t* io2_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
+  if (a_src) {
+    io0_a_src = a_src->data.ptr;
+    io1_a_src = io0_a_src + a_src->meta.ri;
+    iop_a_src = io1_a_src;
+    io2_a_src = io0_a_src + a_src->meta.wi;
   }
 
   uint32_t coro_susp_point = self->private_impl.p_init_dynamic_huffman[0];
@@ -6725,7 +6878,7 @@
     while (v_n_bits < 14) {
       {
         WUFFS_BASE__COROUTINE_SUSPENSION_POINT(1);
-        if (WUFFS_BASE__UNLIKELY(iop_a_src == io1_a_src)) {
+        if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
           status = wuffs_base__suspension__short_read;
           goto suspend;
         }
@@ -6755,7 +6908,7 @@
       while (v_n_bits < 3) {
         {
           WUFFS_BASE__COROUTINE_SUSPENSION_POINT(2);
-          if (WUFFS_BASE__UNLIKELY(iop_a_src == io1_a_src)) {
+          if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
             status = wuffs_base__suspension__short_read;
             goto suspend;
           }
@@ -6794,7 +6947,7 @@
         }
         {
           WUFFS_BASE__COROUTINE_SUSPENSION_POINT(3);
-          if (WUFFS_BASE__UNLIKELY(iop_a_src == io1_a_src)) {
+          if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
             status = wuffs_base__suspension__short_read;
             goto suspend;
           }
@@ -6843,7 +6996,7 @@
       while (v_n_bits < v_n_extra_bits) {
         {
           WUFFS_BASE__COROUTINE_SUSPENSION_POINT(4);
-          if (WUFFS_BASE__UNLIKELY(iop_a_src == io1_a_src)) {
+          if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
             status = wuffs_base__suspension__short_read;
             goto suspend;
           }
@@ -6896,7 +7049,8 @@
 
   goto suspend;
 suspend:
-  self->private_impl.p_init_dynamic_huffman[0] = coro_susp_point;
+  self->private_impl.p_init_dynamic_huffman[0] =
+      wuffs_base__status__is_suspension(status) ? coro_susp_point : 0;
   self->private_data.s_init_dynamic_huffman[0].v_bits = v_bits;
   self->private_data.s_init_dynamic_huffman[0].v_n_bits = v_n_bits;
   self->private_data.s_init_dynamic_huffman[0].v_n_lit = v_n_lit;
@@ -6911,9 +7065,8 @@
 
   goto exit;
 exit:
-  if (a_src.private_impl.buf) {
-    a_src.private_impl.buf->meta.ri =
-        ((size_t)(iop_a_src - a_src.private_impl.buf->data.ptr));
+  if (a_src) {
+    a_src->meta.ri = ((size_t)(iop_a_src - a_src->data.ptr));
   }
 
   return status;
@@ -7178,8 +7331,8 @@
 
 static wuffs_base__status  //
 wuffs_deflate__decoder__decode_huffman_fast(wuffs_deflate__decoder* self,
-                                            wuffs_base__io_writer a_dst,
-                                            wuffs_base__io_reader a_src) {
+                                            wuffs_base__io_buffer* a_dst,
+                                            wuffs_base__io_buffer* a_src) {
   wuffs_base__status status = NULL;
 
   uint32_t v_bits = 0;
@@ -7199,33 +7352,25 @@
   uint8_t* iop_a_dst = NULL;
   uint8_t* io0_a_dst WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
   uint8_t* io1_a_dst WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
-  if (a_dst.private_impl.buf) {
-    iop_a_dst =
-        a_dst.private_impl.buf->data.ptr + a_dst.private_impl.buf->meta.wi;
-    if (!a_dst.private_impl.mark) {
-      a_dst.private_impl.mark = iop_a_dst;
-      a_dst.private_impl.limit =
-          a_dst.private_impl.buf->data.ptr + a_dst.private_impl.buf->data.len;
+  uint8_t* io2_a_dst WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
+  if (a_dst) {
+    io0_a_dst = a_dst->data.ptr;
+    io1_a_dst = io0_a_dst + a_dst->meta.wi;
+    iop_a_dst = io1_a_dst;
+    io2_a_dst = io0_a_dst + a_dst->data.len;
+    if (a_dst->meta.closed) {
+      io2_a_dst = iop_a_dst;
     }
-    if (a_dst.private_impl.buf->meta.closed) {
-      a_dst.private_impl.limit = iop_a_dst;
-    }
-    io0_a_dst = a_dst.private_impl.mark;
-    io1_a_dst = a_dst.private_impl.limit;
   }
   uint8_t* iop_a_src = NULL;
   uint8_t* io0_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
   uint8_t* io1_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
-  if (a_src.private_impl.buf) {
-    iop_a_src =
-        a_src.private_impl.buf->data.ptr + a_src.private_impl.buf->meta.ri;
-    if (!a_src.private_impl.mark) {
-      a_src.private_impl.mark = iop_a_src;
-      a_src.private_impl.limit =
-          a_src.private_impl.buf->data.ptr + a_src.private_impl.buf->meta.wi;
-    }
-    io0_a_src = a_src.private_impl.mark;
-    io1_a_src = a_src.private_impl.limit;
+  uint8_t* io2_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
+  if (a_src) {
+    io0_a_src = a_src->data.ptr;
+    io1_a_src = io0_a_src + a_src->meta.ri;
+    iop_a_src = io1_a_src;
+    io2_a_src = io0_a_src + a_src->meta.wi;
   }
 
   if ((self->private_impl.f_n_bits >= 8) ||
@@ -7238,8 +7383,8 @@
   v_lmask = ((((uint32_t)(1)) << self->private_impl.f_n_huffs_bits[0]) - 1);
   v_dmask = ((((uint32_t)(1)) << self->private_impl.f_n_huffs_bits[1]) - 1);
 label_0_continue:;
-  while ((((uint64_t)(io1_a_dst - iop_a_dst)) >= 258) &&
-         (((uint64_t)(io1_a_src - iop_a_src)) >= 12)) {
+  while ((((uint64_t)(io2_a_dst - iop_a_dst)) >= 258) &&
+         (((uint64_t)(io2_a_src - iop_a_src)) >= 12)) {
     if (v_n_bits < 15) {
       v_bits |= (((uint32_t)(wuffs_base__load_u8be(iop_a_src))) << v_n_bits);
       (iop_a_src += 1, wuffs_base__make_empty_struct());
@@ -7391,11 +7536,10 @@
     v_n_copied = 0;
     while (true) {
       if (((uint64_t)((v_dist_minus_1 + 1))) >
-          ((uint64_t)(iop_a_dst - a_dst.private_impl.mark))) {
+          ((uint64_t)(iop_a_dst - a_dst->data.ptr))) {
         v_hlen = 0;
-        v_hdist =
-            ((uint32_t)((((uint64_t)((v_dist_minus_1 + 1))) -
-                         ((uint64_t)(iop_a_dst - a_dst.private_impl.mark)))));
+        v_hdist = ((uint32_t)((((uint64_t)((v_dist_minus_1 + 1))) -
+                               ((uint64_t)(iop_a_dst - a_dst->data.ptr)))));
         if (v_length > v_hdist) {
           v_length -= v_hdist;
           v_hlen = v_hdist;
@@ -7410,7 +7554,7 @@
         v_hdist = (self->private_impl.f_history_index - v_hdist);
         while (true) {
           v_n_copied = wuffs_base__io_writer__copy_n_from_slice(
-              &iop_a_dst, io1_a_dst, v_hlen,
+              &iop_a_dst, io2_a_dst, v_hlen,
               wuffs_base__slice_u8__subslice_i(
                   wuffs_base__make_slice_u8(self->private_data.f_history,
                                             32768),
@@ -7420,7 +7564,7 @@
           }
           v_hlen -= v_n_copied;
           wuffs_base__io_writer__copy_n_from_slice(
-              &iop_a_dst, io1_a_dst, v_hlen,
+              &iop_a_dst, io2_a_dst, v_hlen,
               wuffs_base__make_slice_u8(self->private_data.f_history, 32768));
           goto label_1_break;
         }
@@ -7429,13 +7573,13 @@
           goto label_0_continue;
         }
         if (((uint64_t)((v_dist_minus_1 + 1))) >
-            ((uint64_t)(iop_a_dst - a_dst.private_impl.mark))) {
+            ((uint64_t)(iop_a_dst - a_dst->data.ptr))) {
           status = wuffs_deflate__error__internal_error_inconsistent_distance;
           goto exit;
         }
       }
       wuffs_base__io_writer__copy_n_from_history_fast(
-          &iop_a_dst, a_dst.private_impl.mark, io1_a_dst, v_length,
+          &iop_a_dst, a_dst->data.ptr, io2_a_dst, v_length,
           (v_dist_minus_1 + 1));
       goto label_2_break;
     }
@@ -7444,7 +7588,7 @@
 label_0_break:;
   while (v_n_bits >= 8) {
     v_n_bits -= 8;
-    if (iop_a_src > io0_a_src) {
+    if (iop_a_src > io1_a_src) {
       (iop_a_src--, wuffs_base__make_empty_struct());
     } else {
       status = wuffs_deflate__error__internal_error_inconsistent_i_o;
@@ -7460,13 +7604,11 @@
   }
   goto exit;
 exit:
-  if (a_dst.private_impl.buf) {
-    a_dst.private_impl.buf->meta.wi =
-        ((size_t)(iop_a_dst - a_dst.private_impl.buf->data.ptr));
+  if (a_dst) {
+    a_dst->meta.wi = ((size_t)(iop_a_dst - a_dst->data.ptr));
   }
-  if (a_src.private_impl.buf) {
-    a_src.private_impl.buf->meta.ri =
-        ((size_t)(iop_a_src - a_src.private_impl.buf->data.ptr));
+  if (a_src) {
+    a_src->meta.ri = ((size_t)(iop_a_src - a_src->data.ptr));
   }
 
   return status;
@@ -7476,8 +7618,8 @@
 
 static wuffs_base__status  //
 wuffs_deflate__decoder__decode_huffman_slow(wuffs_deflate__decoder* self,
-                                            wuffs_base__io_writer a_dst,
-                                            wuffs_base__io_reader a_src) {
+                                            wuffs_base__io_buffer* a_dst,
+                                            wuffs_base__io_buffer* a_src) {
   wuffs_base__status status = NULL;
 
   uint32_t v_bits = 0;
@@ -7503,33 +7645,25 @@
   uint8_t* iop_a_dst = NULL;
   uint8_t* io0_a_dst WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
   uint8_t* io1_a_dst WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
-  if (a_dst.private_impl.buf) {
-    iop_a_dst =
-        a_dst.private_impl.buf->data.ptr + a_dst.private_impl.buf->meta.wi;
-    if (!a_dst.private_impl.mark) {
-      a_dst.private_impl.mark = iop_a_dst;
-      a_dst.private_impl.limit =
-          a_dst.private_impl.buf->data.ptr + a_dst.private_impl.buf->data.len;
+  uint8_t* io2_a_dst WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
+  if (a_dst) {
+    io0_a_dst = a_dst->data.ptr;
+    io1_a_dst = io0_a_dst + a_dst->meta.wi;
+    iop_a_dst = io1_a_dst;
+    io2_a_dst = io0_a_dst + a_dst->data.len;
+    if (a_dst->meta.closed) {
+      io2_a_dst = iop_a_dst;
     }
-    if (a_dst.private_impl.buf->meta.closed) {
-      a_dst.private_impl.limit = iop_a_dst;
-    }
-    io0_a_dst = a_dst.private_impl.mark;
-    io1_a_dst = a_dst.private_impl.limit;
   }
   uint8_t* iop_a_src = NULL;
   uint8_t* io0_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
   uint8_t* io1_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
-  if (a_src.private_impl.buf) {
-    iop_a_src =
-        a_src.private_impl.buf->data.ptr + a_src.private_impl.buf->meta.ri;
-    if (!a_src.private_impl.mark) {
-      a_src.private_impl.mark = iop_a_src;
-      a_src.private_impl.limit =
-          a_src.private_impl.buf->data.ptr + a_src.private_impl.buf->meta.wi;
-    }
-    io0_a_src = a_src.private_impl.mark;
-    io1_a_src = a_src.private_impl.limit;
+  uint8_t* io2_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
+  if (a_src) {
+    io0_a_src = a_src->data.ptr;
+    io1_a_src = io0_a_src + a_src->meta.ri;
+    iop_a_src = io1_a_src;
+    io2_a_src = io0_a_src + a_src->meta.wi;
   }
 
   uint32_t coro_susp_point = self->private_impl.p_decode_huffman_slow[0];
@@ -7573,7 +7707,7 @@
         }
         {
           WUFFS_BASE__COROUTINE_SUSPENSION_POINT(1);
-          if (WUFFS_BASE__UNLIKELY(iop_a_src == io1_a_src)) {
+          if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
             status = wuffs_base__suspension__short_read;
             goto suspend;
           }
@@ -7586,7 +7720,7 @@
     label_1_break:;
       if ((v_table_entry >> 31) != 0) {
         WUFFS_BASE__COROUTINE_SUSPENSION_POINT(2);
-        if (iop_a_dst == io1_a_dst) {
+        if (iop_a_dst == io2_a_dst) {
           status = wuffs_base__suspension__short_write;
           goto suspend;
         }
@@ -7611,7 +7745,7 @@
           }
           {
             WUFFS_BASE__COROUTINE_SUSPENSION_POINT(3);
-            if (WUFFS_BASE__UNLIKELY(iop_a_src == io1_a_src)) {
+            if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
               status = wuffs_base__suspension__short_read;
               goto suspend;
             }
@@ -7624,7 +7758,7 @@
       label_2_break:;
         if ((v_table_entry >> 31) != 0) {
           WUFFS_BASE__COROUTINE_SUSPENSION_POINT(4);
-          if (iop_a_dst == io1_a_dst) {
+          if (iop_a_dst == io2_a_dst) {
             status = wuffs_base__suspension__short_write;
             goto suspend;
           }
@@ -7660,7 +7794,7 @@
         while (v_n_bits < v_table_entry_n_bits) {
           {
             WUFFS_BASE__COROUTINE_SUSPENSION_POINT(5);
-            if (WUFFS_BASE__UNLIKELY(iop_a_src == io1_a_src)) {
+            if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
               status = wuffs_base__suspension__short_read;
               goto suspend;
             }
@@ -7688,7 +7822,7 @@
         }
         {
           WUFFS_BASE__COROUTINE_SUSPENSION_POINT(6);
-          if (WUFFS_BASE__UNLIKELY(iop_a_src == io1_a_src)) {
+          if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
             status = wuffs_base__suspension__short_read;
             goto suspend;
           }
@@ -7714,7 +7848,7 @@
           }
           {
             WUFFS_BASE__COROUTINE_SUSPENSION_POINT(7);
-            if (WUFFS_BASE__UNLIKELY(iop_a_src == io1_a_src)) {
+            if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
               status = wuffs_base__suspension__short_read;
               goto suspend;
             }
@@ -7741,7 +7875,7 @@
         while (v_n_bits < v_table_entry_n_bits) {
           {
             WUFFS_BASE__COROUTINE_SUSPENSION_POINT(8);
-            if (WUFFS_BASE__UNLIKELY(iop_a_src == io1_a_src)) {
+            if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
               status = wuffs_base__suspension__short_read;
               goto suspend;
             }
@@ -7760,10 +7894,9 @@
       }
       while (true) {
         if (((uint64_t)((v_dist_minus_1 + 1))) >
-            ((uint64_t)(iop_a_dst - a_dst.private_impl.mark))) {
-          v_hdist =
-              ((uint32_t)((((uint64_t)((v_dist_minus_1 + 1))) -
-                           ((uint64_t)(iop_a_dst - a_dst.private_impl.mark)))));
+            ((uint64_t)(iop_a_dst - a_dst->data.ptr))) {
+          v_hdist = ((uint32_t)((((uint64_t)((v_dist_minus_1 + 1))) -
+                                 ((uint64_t)(iop_a_dst - a_dst->data.ptr)))));
           if (v_length > v_hdist) {
             v_length -= v_hdist;
             v_hlen = v_hdist;
@@ -7778,7 +7911,7 @@
           v_hdist = (self->private_impl.f_history_index - v_hdist);
           while (true) {
             v_n_copied = wuffs_base__io_writer__copy_n_from_slice(
-                &iop_a_dst, io1_a_dst, v_hlen,
+                &iop_a_dst, io2_a_dst, v_hlen,
                 wuffs_base__slice_u8__subslice_i(
                     wuffs_base__make_slice_u8(self->private_data.f_history,
                                               32768),
@@ -7801,7 +7934,7 @@
           if (v_hlen > 0) {
             while (true) {
               v_n_copied = wuffs_base__io_writer__copy_n_from_slice(
-                  &iop_a_dst, io1_a_dst, v_hlen,
+                  &iop_a_dst, io2_a_dst, v_hlen,
                   wuffs_base__slice_u8__subslice_i(
                       wuffs_base__make_slice_u8(self->private_data.f_history,
                                                 32768),
@@ -7822,7 +7955,7 @@
           }
         }
         v_n_copied = wuffs_base__io_writer__copy_n_from_history(
-            &iop_a_dst, a_dst.private_impl.mark, io1_a_dst, v_length,
+            &iop_a_dst, a_dst->data.ptr, io2_a_dst, v_length,
             (v_dist_minus_1 + 1));
         if (v_length <= v_n_copied) {
           v_length = 0;
@@ -7852,7 +7985,8 @@
 
   goto suspend;
 suspend:
-  self->private_impl.p_decode_huffman_slow[0] = coro_susp_point;
+  self->private_impl.p_decode_huffman_slow[0] =
+      wuffs_base__status__is_suspension(status) ? coro_susp_point : 0;
   self->private_data.s_decode_huffman_slow[0].v_bits = v_bits;
   self->private_data.s_decode_huffman_slow[0].v_n_bits = v_n_bits;
   self->private_data.s_decode_huffman_slow[0].v_table_entry = v_table_entry;
@@ -7869,13 +8003,11 @@
 
   goto exit;
 exit:
-  if (a_dst.private_impl.buf) {
-    a_dst.private_impl.buf->meta.wi =
-        ((size_t)(iop_a_dst - a_dst.private_impl.buf->data.ptr));
+  if (a_dst) {
+    a_dst->meta.wi = ((size_t)(iop_a_dst - a_dst->data.ptr));
   }
-  if (a_src.private_impl.buf) {
-    a_src.private_impl.buf->meta.ri =
-        ((size_t)(iop_a_src - a_src.private_impl.buf->data.ptr));
+  if (a_src) {
+    a_src->meta.ri = ((size_t)(iop_a_src - a_src->data.ptr));
   }
 
   return status;
@@ -7900,11 +8032,11 @@
 
 static wuffs_base__empty_struct  //
 wuffs_lzw__decoder__read_from(wuffs_lzw__decoder* self,
-                              wuffs_base__io_reader a_src);
+                              wuffs_base__io_buffer* a_src);
 
 static wuffs_base__status  //
 wuffs_lzw__decoder__write_to(wuffs_lzw__decoder* self,
-                             wuffs_base__io_writer a_dst);
+                             wuffs_base__io_buffer* a_dst);
 
 // ---------------- Initializer Implementations
 
@@ -7970,12 +8102,12 @@
   if (self->private_impl.magic != WUFFS_BASE__MAGIC) {
     return wuffs_base__make_empty_struct();
   }
-  if (a_lw < 2 || a_lw > 8) {
+  if (a_lw > 8) {
     self->private_impl.magic = WUFFS_BASE__DISABLED;
     return wuffs_base__make_empty_struct();
   }
 
-  self->private_impl.f_set_literal_width_arg = a_lw;
+  self->private_impl.f_set_literal_width_arg = (a_lw + 1);
   return wuffs_base__make_empty_struct();
 }
 
@@ -7998,8 +8130,8 @@
 
 WUFFS_BASE__MAYBE_STATIC wuffs_base__status  //
 wuffs_lzw__decoder__decode_io_writer(wuffs_lzw__decoder* self,
-                                     wuffs_base__io_writer a_dst,
-                                     wuffs_base__io_reader a_src,
+                                     wuffs_base__io_buffer* a_dst,
+                                     wuffs_base__io_buffer* a_src,
                                      wuffs_base__slice_u8 a_workbuf) {
   if (!self) {
     return wuffs_base__error__bad_receiver;
@@ -8009,6 +8141,10 @@
                ? wuffs_base__error__disabled_by_previous_error
                : wuffs_base__error__initialize_not_called;
   }
+  if (!a_dst || !a_src) {
+    self->private_impl.magic = WUFFS_BASE__DISABLED;
+    return wuffs_base__error__bad_argument;
+  }
   if ((self->private_impl.active_coroutine != 0) &&
       (self->private_impl.active_coroutine != 1)) {
     self->private_impl.magic = WUFFS_BASE__DISABLED;
@@ -8026,9 +8162,9 @@
     WUFFS_BASE__COROUTINE_SUSPENSION_POINT_0;
 
     self->private_impl.f_literal_width = 8;
-    if (self->private_impl.f_set_literal_width_arg >= 2) {
+    if (self->private_impl.f_set_literal_width_arg > 0) {
       self->private_impl.f_literal_width =
-          self->private_impl.f_set_literal_width_arg;
+          (self->private_impl.f_set_literal_width_arg - 1);
     }
     self->private_impl.f_clear_code =
         (((uint32_t)(1)) << self->private_impl.f_literal_width);
@@ -8081,8 +8217,10 @@
 
   goto suspend;
 suspend:
-  self->private_impl.p_decode_io_writer[0] = coro_susp_point;
-  self->private_impl.active_coroutine = 1;
+  self->private_impl.p_decode_io_writer[0] =
+      wuffs_base__status__is_suspension(status) ? coro_susp_point : 0;
+  self->private_impl.active_coroutine =
+      wuffs_base__status__is_suspension(status) ? 1 : 0;
 
   goto exit;
 exit:
@@ -8096,7 +8234,7 @@
 
 static wuffs_base__empty_struct  //
 wuffs_lzw__decoder__read_from(wuffs_lzw__decoder* self,
-                              wuffs_base__io_reader a_src) {
+                              wuffs_base__io_buffer* a_src) {
   uint32_t v_clear_code = 0;
   uint32_t v_end_code = 0;
   uint32_t v_save_code = 0;
@@ -8116,16 +8254,12 @@
   uint8_t* iop_a_src = NULL;
   uint8_t* io0_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
   uint8_t* io1_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
-  if (a_src.private_impl.buf) {
-    iop_a_src =
-        a_src.private_impl.buf->data.ptr + a_src.private_impl.buf->meta.ri;
-    if (!a_src.private_impl.mark) {
-      a_src.private_impl.mark = iop_a_src;
-      a_src.private_impl.limit =
-          a_src.private_impl.buf->data.ptr + a_src.private_impl.buf->meta.wi;
-    }
-    io0_a_src = a_src.private_impl.mark;
-    io1_a_src = a_src.private_impl.limit;
+  uint8_t* io2_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
+  if (a_src) {
+    io0_a_src = a_src->data.ptr;
+    io1_a_src = io0_a_src + a_src->meta.ri;
+    iop_a_src = io1_a_src;
+    io2_a_src = io0_a_src + a_src->meta.wi;
   }
 
   v_clear_code = self->private_impl.f_clear_code;
@@ -8138,11 +8272,11 @@
   v_output_wi = self->private_impl.f_output_wi;
   while (true) {
     if (v_n_bits < v_width) {
-      if (((uint64_t)(io1_a_src - iop_a_src)) >= 4) {
+      if (((uint64_t)(io2_a_src - iop_a_src)) >= 4) {
         v_bits |= (wuffs_base__load_u32le(iop_a_src) << v_n_bits);
         (iop_a_src += ((31 - v_n_bits) >> 3), wuffs_base__make_empty_struct());
         v_n_bits |= 24;
-      } else if (((uint64_t)(io1_a_src - iop_a_src)) <= 0) {
+      } else if (((uint64_t)(io2_a_src - iop_a_src)) <= 0) {
         self->private_impl.f_read_from_return_value = 2;
         goto label_0_break;
       } else {
@@ -8150,7 +8284,7 @@
         (iop_a_src += 1, wuffs_base__make_empty_struct());
         v_n_bits += 8;
         if (v_n_bits >= v_width) {
-        } else if (((uint64_t)(io1_a_src - iop_a_src)) <= 0) {
+        } else if (((uint64_t)(io2_a_src - iop_a_src)) <= 0) {
           self->private_impl.f_read_from_return_value = 2;
           goto label_0_break;
         } else {
@@ -8265,7 +8399,7 @@
   if (self->private_impl.f_read_from_return_value != 2) {
     while (v_n_bits >= 8) {
       v_n_bits -= 8;
-      if (iop_a_src > io0_a_src) {
+      if (iop_a_src > io1_a_src) {
         (iop_a_src--, wuffs_base__make_empty_struct());
       } else {
         self->private_impl.f_read_from_return_value = 4;
@@ -8280,9 +8414,8 @@
   self->private_impl.f_bits = v_bits;
   self->private_impl.f_n_bits = v_n_bits;
   self->private_impl.f_output_wi = v_output_wi;
-  if (a_src.private_impl.buf) {
-    a_src.private_impl.buf->meta.ri =
-        ((size_t)(iop_a_src - a_src.private_impl.buf->data.ptr));
+  if (a_src) {
+    a_src->meta.ri = ((size_t)(iop_a_src - a_src->data.ptr));
   }
 
   return wuffs_base__make_empty_struct();
@@ -8292,7 +8425,7 @@
 
 static wuffs_base__status  //
 wuffs_lzw__decoder__write_to(wuffs_lzw__decoder* self,
-                             wuffs_base__io_writer a_dst) {
+                             wuffs_base__io_buffer* a_dst) {
   wuffs_base__status status = NULL;
 
   wuffs_base__slice_u8 v_s = {0};
@@ -8301,19 +8434,15 @@
   uint8_t* iop_a_dst = NULL;
   uint8_t* io0_a_dst WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
   uint8_t* io1_a_dst WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
-  if (a_dst.private_impl.buf) {
-    iop_a_dst =
-        a_dst.private_impl.buf->data.ptr + a_dst.private_impl.buf->meta.wi;
-    if (!a_dst.private_impl.mark) {
-      a_dst.private_impl.mark = iop_a_dst;
-      a_dst.private_impl.limit =
-          a_dst.private_impl.buf->data.ptr + a_dst.private_impl.buf->data.len;
+  uint8_t* io2_a_dst WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
+  if (a_dst) {
+    io0_a_dst = a_dst->data.ptr;
+    io1_a_dst = io0_a_dst + a_dst->meta.wi;
+    iop_a_dst = io1_a_dst;
+    io2_a_dst = io0_a_dst + a_dst->data.len;
+    if (a_dst->meta.closed) {
+      io2_a_dst = iop_a_dst;
     }
-    if (a_dst.private_impl.buf->meta.closed) {
-      a_dst.private_impl.limit = iop_a_dst;
-    }
-    io0_a_dst = a_dst.private_impl.mark;
-    io1_a_dst = a_dst.private_impl.limit;
   }
 
   uint32_t coro_susp_point = self->private_impl.p_write_to[0];
@@ -8330,7 +8459,7 @@
       v_s = wuffs_base__slice_u8__subslice_ij(
           wuffs_base__make_slice_u8(self->private_data.f_output, 8199),
           self->private_impl.f_output_ri, self->private_impl.f_output_wi);
-      v_n = wuffs_base__io_writer__copy_from_slice(&iop_a_dst, io1_a_dst, v_s);
+      v_n = wuffs_base__io_writer__copy_from_slice(&iop_a_dst, io2_a_dst, v_s);
       if (v_n == ((uint64_t)(v_s.len))) {
         self->private_impl.f_output_ri = 0;
         self->private_impl.f_output_wi = 0;
@@ -8352,13 +8481,13 @@
 
   goto suspend;
 suspend:
-  self->private_impl.p_write_to[0] = coro_susp_point;
+  self->private_impl.p_write_to[0] =
+      wuffs_base__status__is_suspension(status) ? coro_susp_point : 0;
 
   goto exit;
 exit:
-  if (a_dst.private_impl.buf) {
-    a_dst.private_impl.buf->meta.wi =
-        ((size_t)(iop_a_dst - a_dst.private_impl.buf->data.ptr));
+  if (a_dst) {
+    a_dst->meta.wi = ((size_t)(iop_a_dst - a_dst->data.ptr));
   }
 
   return status;
@@ -8395,9 +8524,11 @@
 
 const char* wuffs_gif__error__bad_block = "#gif: bad block";
 const char* wuffs_gif__error__bad_extension_label = "#gif: bad extension label";
+const char* wuffs_gif__error__bad_frame_size = "#gif: bad frame size";
 const char* wuffs_gif__error__bad_graphic_control = "#gif: bad graphic control";
 const char* wuffs_gif__error__bad_header = "#gif: bad header";
 const char* wuffs_gif__error__bad_literal_width = "#gif: bad literal width";
+const char* wuffs_gif__error__bad_palette = "#gif: bad palette";
 const char* wuffs_gif__error__internal_error_inconsistent_ri_wi =
     "#gif: internal error: inconsistent ri/wi";
 
@@ -8427,58 +8558,70 @@
         78, 69, 84, 83, 67, 65, 80, 69, 50, 46, 48,
 };
 
+static const uint8_t            //
+    wuffs_gif__iccrgbg1012[11]  //
+    WUFFS_BASE__POTENTIALLY_UNUSED = {
+        73, 67, 67, 82, 71, 66, 71, 49, 48, 49, 50,
+};
+
+static const uint8_t           //
+    wuffs_gif__xmpdataxmp[11]  //
+    WUFFS_BASE__POTENTIALLY_UNUSED = {
+        88, 77, 80, 32, 68, 97, 116, 97, 88, 77, 80,
+};
+
 // ---------------- Private Initializer Prototypes
 
 // ---------------- Private Function Prototypes
 
 static wuffs_base__status  //
 wuffs_gif__decoder__skip_frame(wuffs_gif__decoder* self,
-                               wuffs_base__io_reader a_src);
+                               wuffs_base__io_buffer* a_src);
 
 static wuffs_base__empty_struct  //
 wuffs_gif__decoder__reset_gc(wuffs_gif__decoder* self);
 
 static wuffs_base__status  //
 wuffs_gif__decoder__decode_up_to_id_part1(wuffs_gif__decoder* self,
-                                          wuffs_base__io_reader a_src);
+                                          wuffs_base__io_buffer* a_src);
 
 static wuffs_base__status  //
 wuffs_gif__decoder__decode_header(wuffs_gif__decoder* self,
-                                  wuffs_base__io_reader a_src);
+                                  wuffs_base__io_buffer* a_src);
 
 static wuffs_base__status  //
 wuffs_gif__decoder__decode_lsd(wuffs_gif__decoder* self,
-                               wuffs_base__io_reader a_src);
+                               wuffs_base__io_buffer* a_src);
 
 static wuffs_base__status  //
 wuffs_gif__decoder__decode_extension(wuffs_gif__decoder* self,
-                                     wuffs_base__io_reader a_src);
+                                     wuffs_base__io_buffer* a_src);
 
 static wuffs_base__status  //
 wuffs_gif__decoder__skip_blocks(wuffs_gif__decoder* self,
-                                wuffs_base__io_reader a_src);
+                                wuffs_base__io_buffer* a_src);
 
 static wuffs_base__status  //
 wuffs_gif__decoder__decode_ae(wuffs_gif__decoder* self,
-                              wuffs_base__io_reader a_src);
+                              wuffs_base__io_buffer* a_src);
 
 static wuffs_base__status  //
 wuffs_gif__decoder__decode_gc(wuffs_gif__decoder* self,
-                              wuffs_base__io_reader a_src);
+                              wuffs_base__io_buffer* a_src);
 
 static wuffs_base__status  //
 wuffs_gif__decoder__decode_id_part0(wuffs_gif__decoder* self,
-                                    wuffs_base__io_reader a_src);
+                                    wuffs_base__io_buffer* a_src);
 
 static wuffs_base__status  //
 wuffs_gif__decoder__decode_id_part1(wuffs_gif__decoder* self,
                                     wuffs_base__pixel_buffer* a_dst,
-                                    wuffs_base__io_reader a_src);
+                                    wuffs_base__io_buffer* a_src);
 
 static wuffs_base__status  //
 wuffs_gif__decoder__decode_id_part2(wuffs_gif__decoder* self,
                                     wuffs_base__pixel_buffer* a_dst,
-                                    wuffs_base__io_reader a_src,
+                                    wuffs_base__io_buffer* a_src,
                                     wuffs_base__slice_u8 a_workbuf);
 
 static wuffs_base__status  //
@@ -8548,12 +8691,47 @@
 
 // ---------------- Function Implementations
 
+// -------- func gif.decoder.set_quirk_enabled
+
+WUFFS_BASE__MAYBE_STATIC wuffs_base__empty_struct  //
+wuffs_gif__decoder__set_quirk_enabled(wuffs_gif__decoder* self,
+                                      uint32_t a_quirk,
+                                      bool a_enabled) {
+  if (!self) {
+    return wuffs_base__make_empty_struct();
+  }
+  if (self->private_impl.magic != WUFFS_BASE__MAGIC) {
+    return wuffs_base__make_empty_struct();
+  }
+
+  if (self->private_impl.f_call_sequence == 0) {
+    if (a_quirk == 1041635328) {
+      self->private_impl.f_quirk_enabled_delay_num_decoded_frames = a_enabled;
+    } else if (a_quirk == 1041635329) {
+      self->private_impl
+          .f_quirk_enabled_first_frame_local_palette_means_black_background =
+          a_enabled;
+    } else if (a_quirk == 1041635330) {
+      self->private_impl.f_quirk_enabled_honor_background_color = a_enabled;
+    } else if (a_quirk == 1041635331) {
+      self->private_impl.f_quirk_enabled_ignore_too_much_pixel_data = a_enabled;
+    } else if (a_quirk == 1041635332) {
+      self->private_impl.f_quirk_enabled_image_bounds_are_strict = a_enabled;
+    } else if (a_quirk == 1041635333) {
+      self->private_impl.f_quirk_enabled_reject_empty_frame = a_enabled;
+    } else if (a_quirk == 1041635334) {
+      self->private_impl.f_quirk_enabled_reject_empty_palette = a_enabled;
+    }
+  }
+  return wuffs_base__make_empty_struct();
+}
+
 // -------- func gif.decoder.decode_image_config
 
 WUFFS_BASE__MAYBE_STATIC wuffs_base__status  //
 wuffs_gif__decoder__decode_image_config(wuffs_gif__decoder* self,
                                         wuffs_base__image_config* a_dst,
-                                        wuffs_base__io_reader a_src) {
+                                        wuffs_base__io_buffer* a_src) {
   if (!self) {
     return wuffs_base__error__bad_receiver;
   }
@@ -8562,6 +8740,10 @@
                ? wuffs_base__error__disabled_by_previous_error
                : wuffs_base__error__initialize_not_called;
   }
+  if (!a_src) {
+    self->private_impl.magic = WUFFS_BASE__DISABLED;
+    return wuffs_base__error__bad_argument;
+  }
   if ((self->private_impl.active_coroutine != 0) &&
       (self->private_impl.active_coroutine != 1)) {
     self->private_impl.magic = WUFFS_BASE__DISABLED;
@@ -8578,38 +8760,47 @@
   switch (coro_susp_point) {
     WUFFS_BASE__COROUTINE_SUSPENSION_POINT_0;
 
-    if (self->private_impl.f_call_sequence >= 1) {
+    if (self->private_impl.f_call_sequence == 0) {
+      WUFFS_BASE__COROUTINE_SUSPENSION_POINT(1);
+      status = wuffs_gif__decoder__decode_header(self, a_src);
+      if (status) {
+        goto suspend;
+      }
+      WUFFS_BASE__COROUTINE_SUSPENSION_POINT(2);
+      status = wuffs_gif__decoder__decode_lsd(self, a_src);
+      if (status) {
+        goto suspend;
+      }
+    } else if (self->private_impl.f_call_sequence != 2) {
       status = wuffs_base__error__bad_call_sequence;
       goto exit;
     }
-    WUFFS_BASE__COROUTINE_SUSPENSION_POINT(1);
-    status = wuffs_gif__decoder__decode_header(self, a_src);
-    if (status) {
-      goto suspend;
-    }
-    WUFFS_BASE__COROUTINE_SUSPENSION_POINT(2);
-    status = wuffs_gif__decoder__decode_lsd(self, a_src);
-    if (status) {
-      goto suspend;
-    }
     WUFFS_BASE__COROUTINE_SUSPENSION_POINT(3);
     status = wuffs_gif__decoder__decode_up_to_id_part1(self, a_src);
     if (status) {
       goto suspend;
     }
-    v_ffio =
-        (!self->private_impl.f_gc_has_transparent_index &&
-         (self->private_impl.f_frame_rect_x0 == 0) &&
-         (self->private_impl.f_frame_rect_y0 == 0) &&
-         (self->private_impl.f_frame_rect_x1 == self->private_impl.f_width) &&
-         (self->private_impl.f_frame_rect_y1 == self->private_impl.f_height));
+    v_ffio = !self->private_impl.f_gc_has_transparent_index;
+    if (!self->private_impl.f_quirk_enabled_honor_background_color) {
+      v_ffio =
+          (v_ffio && (self->private_impl.f_frame_rect_x0 == 0) &&
+           (self->private_impl.f_frame_rect_y0 == 0) &&
+           (self->private_impl.f_frame_rect_x1 == self->private_impl.f_width) &&
+           (self->private_impl.f_frame_rect_y1 == self->private_impl.f_height));
+    } else if (v_ffio) {
+      self->private_impl.f_black_color_u32_argb_premul = 4278190080;
+    }
+    if (self->private_impl.f_background_color_u32_argb_premul == 77) {
+      self->private_impl.f_background_color_u32_argb_premul =
+          self->private_impl.f_black_color_u32_argb_premul;
+    }
     if (a_dst != NULL) {
       wuffs_base__image_config__set(
           a_dst, 1191444488, 0, self->private_impl.f_width,
           self->private_impl.f_height,
           self->private_impl.f_frame_config_io_position, v_ffio);
     }
-    self->private_impl.f_call_sequence = 1;
+    self->private_impl.f_call_sequence = 3;
 
     goto ok;
   ok:
@@ -8619,8 +8810,10 @@
 
   goto suspend;
 suspend:
-  self->private_impl.p_decode_image_config[0] = coro_susp_point;
-  self->private_impl.active_coroutine = 1;
+  self->private_impl.p_decode_image_config[0] =
+      wuffs_base__status__is_suspension(status) ? coro_susp_point : 0;
+  self->private_impl.active_coroutine =
+      wuffs_base__status__is_suspension(status) ? 1 : 0;
 
   goto exit;
 exit:
@@ -8630,6 +8823,172 @@
   return status;
 }
 
+// -------- func gif.decoder.set_report_metadata
+
+WUFFS_BASE__MAYBE_STATIC wuffs_base__empty_struct  //
+wuffs_gif__decoder__set_report_metadata(wuffs_gif__decoder* self,
+                                        uint32_t a_fourcc,
+                                        bool a_report) {
+  if (!self) {
+    return wuffs_base__make_empty_struct();
+  }
+  if (self->private_impl.magic != WUFFS_BASE__MAGIC) {
+    return wuffs_base__make_empty_struct();
+  }
+
+  if (a_fourcc == 1229144912) {
+    self->private_impl.f_report_metadata_iccp = a_report;
+  } else if (a_fourcc == 1481461792) {
+    self->private_impl.f_report_metadata_xmp = a_report;
+  }
+  return wuffs_base__make_empty_struct();
+}
+
+// -------- func gif.decoder.ack_metadata_chunk
+
+WUFFS_BASE__MAYBE_STATIC wuffs_base__status  //
+wuffs_gif__decoder__ack_metadata_chunk(wuffs_gif__decoder* self,
+                                       wuffs_base__io_buffer* a_src) {
+  if (!self) {
+    return wuffs_base__error__bad_receiver;
+  }
+  if (self->private_impl.magic != WUFFS_BASE__MAGIC) {
+    return (self->private_impl.magic == WUFFS_BASE__DISABLED)
+               ? wuffs_base__error__disabled_by_previous_error
+               : wuffs_base__error__initialize_not_called;
+  }
+  if (!a_src) {
+    self->private_impl.magic = WUFFS_BASE__DISABLED;
+    return wuffs_base__error__bad_argument;
+  }
+  if ((self->private_impl.active_coroutine != 0) &&
+      (self->private_impl.active_coroutine != 2)) {
+    self->private_impl.magic = WUFFS_BASE__DISABLED;
+    return wuffs_base__error__interleaved_coroutine_calls;
+  }
+  self->private_impl.active_coroutine = 0;
+  wuffs_base__status status = NULL;
+
+  uint8_t* iop_a_src = NULL;
+  uint8_t* io0_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
+  uint8_t* io1_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
+  uint8_t* io2_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
+  if (a_src) {
+    io0_a_src = a_src->data.ptr;
+    io1_a_src = io0_a_src + a_src->meta.ri;
+    iop_a_src = io1_a_src;
+    io2_a_src = io0_a_src + a_src->meta.wi;
+  }
+
+  uint32_t coro_susp_point = self->private_impl.p_ack_metadata_chunk[0];
+  if (coro_susp_point) {
+  }
+  switch (coro_susp_point) {
+    WUFFS_BASE__COROUTINE_SUSPENSION_POINT_0;
+
+    if (self->private_impl.f_call_sequence != 1) {
+      status = wuffs_base__error__bad_call_sequence;
+      goto exit;
+    }
+    if ((a_src ? wuffs_base__u64__sat_add(
+                     a_src->meta.pos, ((uint64_t)(iop_a_src - a_src->data.ptr)))
+               : 0) != self->private_impl.f_metadata_io_position) {
+      status = wuffs_base__error__bad_i_o_position;
+      goto exit;
+    }
+    while (((uint64_t)(io2_a_src - iop_a_src)) <= 0) {
+      status = wuffs_base__suspension__short_read;
+      WUFFS_BASE__COROUTINE_SUSPENSION_POINT_MAYBE_SUSPEND(1);
+    }
+    if (self->private_impl.f_metadata_fourcc_value == 1481461792) {
+      self->private_impl.f_metadata_chunk_length_value =
+          (((uint64_t)(wuffs_base__load_u8be(iop_a_src))) + 1);
+      if (self->private_impl.f_metadata_chunk_length_value > 1) {
+        self->private_impl.f_metadata_io_position = wuffs_base__u64__sat_add(
+            (a_src ? wuffs_base__u64__sat_add(
+                         a_src->meta.pos,
+                         ((uint64_t)(iop_a_src - a_src->data.ptr)))
+                   : 0),
+            self->private_impl.f_metadata_chunk_length_value);
+        status = wuffs_base__warning__metadata_reported;
+        goto ok;
+      }
+    } else {
+      self->private_impl.f_metadata_chunk_length_value =
+          ((uint64_t)(wuffs_base__load_u8be(iop_a_src)));
+      if (self->private_impl.f_metadata_chunk_length_value > 0) {
+        (iop_a_src += 1, wuffs_base__make_empty_struct());
+        self->private_impl.f_metadata_io_position = wuffs_base__u64__sat_add(
+            (a_src ? wuffs_base__u64__sat_add(
+                         a_src->meta.pos,
+                         ((uint64_t)(iop_a_src - a_src->data.ptr)))
+                   : 0),
+            self->private_impl.f_metadata_chunk_length_value);
+        status = wuffs_base__warning__metadata_reported;
+        goto ok;
+      }
+    }
+    (iop_a_src += 1, wuffs_base__make_empty_struct());
+    self->private_impl.f_call_sequence = 2;
+    self->private_impl.f_metadata_fourcc_value = 0;
+    self->private_impl.f_metadata_io_position = 0;
+    status = NULL;
+    goto ok;
+    goto ok;
+  ok:
+    self->private_impl.p_ack_metadata_chunk[0] = 0;
+    goto exit;
+  }
+
+  goto suspend;
+suspend:
+  self->private_impl.p_ack_metadata_chunk[0] =
+      wuffs_base__status__is_suspension(status) ? coro_susp_point : 0;
+  self->private_impl.active_coroutine =
+      wuffs_base__status__is_suspension(status) ? 2 : 0;
+
+  goto exit;
+exit:
+  if (a_src) {
+    a_src->meta.ri = ((size_t)(iop_a_src - a_src->data.ptr));
+  }
+
+  if (wuffs_base__status__is_error(status)) {
+    self->private_impl.magic = WUFFS_BASE__DISABLED;
+  }
+  return status;
+}
+
+// -------- func gif.decoder.metadata_fourcc
+
+WUFFS_BASE__MAYBE_STATIC uint32_t  //
+wuffs_gif__decoder__metadata_fourcc(const wuffs_gif__decoder* self) {
+  if (!self) {
+    return 0;
+  }
+  if ((self->private_impl.magic != WUFFS_BASE__MAGIC) &&
+      (self->private_impl.magic != WUFFS_BASE__DISABLED)) {
+    return 0;
+  }
+
+  return self->private_impl.f_metadata_fourcc_value;
+}
+
+// -------- func gif.decoder.metadata_chunk_length
+
+WUFFS_BASE__MAYBE_STATIC uint64_t  //
+wuffs_gif__decoder__metadata_chunk_length(const wuffs_gif__decoder* self) {
+  if (!self) {
+    return 0;
+  }
+  if ((self->private_impl.magic != WUFFS_BASE__MAGIC) &&
+      (self->private_impl.magic != WUFFS_BASE__DISABLED)) {
+    return 0;
+  }
+
+  return self->private_impl.f_metadata_chunk_length_value;
+}
+
 // -------- func gif.decoder.num_animation_loops
 
 WUFFS_BASE__MAYBE_STATIC uint32_t  //
@@ -8732,6 +9091,7 @@
   if (self->private_impl.f_call_sequence == 0) {
     return wuffs_base__error__bad_call_sequence;
   }
+  self->private_impl.f_delayed_num_decoded_frames = false;
   self->private_impl.f_end_of_data = false;
   self->private_impl.f_restarted = true;
   self->private_impl.f_frame_config_io_position = a_io_position;
@@ -8746,7 +9106,7 @@
 WUFFS_BASE__MAYBE_STATIC wuffs_base__status  //
 wuffs_gif__decoder__decode_frame_config(wuffs_gif__decoder* self,
                                         wuffs_base__frame_config* a_dst,
-                                        wuffs_base__io_reader a_src) {
+                                        wuffs_base__io_buffer* a_src) {
   if (!self) {
     return wuffs_base__error__bad_receiver;
   }
@@ -8755,8 +9115,12 @@
                ? wuffs_base__error__disabled_by_previous_error
                : wuffs_base__error__initialize_not_called;
   }
+  if (!a_src) {
+    self->private_impl.magic = WUFFS_BASE__DISABLED;
+    return wuffs_base__error__bad_argument;
+  }
   if ((self->private_impl.active_coroutine != 0) &&
-      (self->private_impl.active_coroutine != 2)) {
+      (self->private_impl.active_coroutine != 3)) {
     self->private_impl.magic = WUFFS_BASE__DISABLED;
     return wuffs_base__error__interleaved_coroutine_calls;
   }
@@ -8764,32 +9128,67 @@
   wuffs_base__status status = NULL;
 
   uint8_t v_blend = 0;
+  uint32_t v_background_color = 0;
+  uint8_t v_flags = 0;
+
+  uint8_t* iop_a_src = NULL;
+  uint8_t* io0_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
+  uint8_t* io1_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
+  uint8_t* io2_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
+  if (a_src) {
+    io0_a_src = a_src->data.ptr;
+    io1_a_src = io0_a_src + a_src->meta.ri;
+    iop_a_src = io1_a_src;
+    io2_a_src = io0_a_src + a_src->meta.wi;
+  }
 
   uint32_t coro_susp_point = self->private_impl.p_decode_frame_config[0];
   if (coro_susp_point) {
+    v_blend = self->private_data.s_decode_frame_config[0].v_blend;
+    v_background_color =
+        self->private_data.s_decode_frame_config[0].v_background_color;
   }
   switch (coro_susp_point) {
     WUFFS_BASE__COROUTINE_SUSPENSION_POINT_0;
 
+    self->private_impl.f_ignore_metadata = true;
     (memset(&self->private_impl.f_dirty_y, 0, sizeof(wuffs_base__range_ie_u32)),
      wuffs_base__make_empty_struct());
     if (!self->private_impl.f_end_of_data) {
       if (self->private_impl.f_call_sequence == 0) {
+        if (a_src) {
+          a_src->meta.ri = ((size_t)(iop_a_src - a_src->data.ptr));
+        }
         WUFFS_BASE__COROUTINE_SUSPENSION_POINT(1);
         status = wuffs_gif__decoder__decode_image_config(self, NULL, a_src);
+        if (a_src) {
+          iop_a_src = a_src->data.ptr + a_src->meta.ri;
+        }
         if (status) {
           goto suspend;
         }
-      } else if (self->private_impl.f_call_sequence != 1) {
-        if (self->private_impl.f_call_sequence == 2) {
+      } else if (self->private_impl.f_call_sequence != 3) {
+        if (self->private_impl.f_call_sequence == 4) {
+          if (a_src) {
+            a_src->meta.ri = ((size_t)(iop_a_src - a_src->data.ptr));
+          }
           WUFFS_BASE__COROUTINE_SUSPENSION_POINT(2);
           status = wuffs_gif__decoder__skip_frame(self, a_src);
+          if (a_src) {
+            iop_a_src = a_src->data.ptr + a_src->meta.ri;
+          }
           if (status) {
             goto suspend;
           }
         }
+        if (a_src) {
+          a_src->meta.ri = ((size_t)(iop_a_src - a_src->data.ptr));
+        }
         WUFFS_BASE__COROUTINE_SUSPENSION_POINT(3);
         status = wuffs_gif__decoder__decode_up_to_id_part1(self, a_src);
+        if (a_src) {
+          iop_a_src = a_src->data.ptr + a_src->meta.ri;
+        }
         if (status) {
           goto suspend;
         }
@@ -8800,8 +9199,23 @@
       goto ok;
     }
     v_blend = 0;
+    v_background_color = self->private_impl.f_black_color_u32_argb_premul;
     if (!self->private_impl.f_gc_has_transparent_index) {
       v_blend = 2;
+      v_background_color =
+          self->private_impl.f_background_color_u32_argb_premul;
+      if (self->private_impl
+              .f_quirk_enabled_first_frame_local_palette_means_black_background &&
+          (self->private_impl.f_num_decoded_frame_configs_value == 0)) {
+        while (((uint64_t)(io2_a_src - iop_a_src)) <= 0) {
+          status = wuffs_base__suspension__short_read;
+          WUFFS_BASE__COROUTINE_SUSPENSION_POINT_MAYBE_SUSPEND(4);
+        }
+        v_flags = wuffs_base__load_u8be(iop_a_src);
+        if ((v_flags & 128) != 0) {
+          v_background_color = self->private_impl.f_black_color_u32_argb_premul;
+        }
+      }
     }
     if (a_dst != NULL) {
       wuffs_base__frame_config__update(
@@ -8818,11 +9232,11 @@
           ((wuffs_base__flicks)(self->private_impl.f_gc_duration)),
           self->private_impl.f_num_decoded_frame_configs_value,
           self->private_impl.f_frame_config_io_position, v_blend,
-          self->private_impl.f_gc_disposal);
+          self->private_impl.f_gc_disposal, v_background_color);
     }
     wuffs_base__u64__sat_add_indirect(
         &self->private_impl.f_num_decoded_frame_configs_value, 1);
-    self->private_impl.f_call_sequence = 2;
+    self->private_impl.f_call_sequence = 4;
 
     goto ok;
   ok:
@@ -8832,11 +9246,20 @@
 
   goto suspend;
 suspend:
-  self->private_impl.p_decode_frame_config[0] = coro_susp_point;
-  self->private_impl.active_coroutine = 2;
+  self->private_impl.p_decode_frame_config[0] =
+      wuffs_base__status__is_suspension(status) ? coro_susp_point : 0;
+  self->private_impl.active_coroutine =
+      wuffs_base__status__is_suspension(status) ? 3 : 0;
+  self->private_data.s_decode_frame_config[0].v_blend = v_blend;
+  self->private_data.s_decode_frame_config[0].v_background_color =
+      v_background_color;
 
   goto exit;
 exit:
+  if (a_src) {
+    a_src->meta.ri = ((size_t)(iop_a_src - a_src->data.ptr));
+  }
+
   if (wuffs_base__status__is_error(status)) {
     self->private_impl.magic = WUFFS_BASE__DISABLED;
   }
@@ -8847,24 +9270,21 @@
 
 static wuffs_base__status  //
 wuffs_gif__decoder__skip_frame(wuffs_gif__decoder* self,
-                               wuffs_base__io_reader a_src) {
+                               wuffs_base__io_buffer* a_src) {
   wuffs_base__status status = NULL;
 
   uint8_t v_flags = 0;
+  uint8_t v_lw = 0;
 
   uint8_t* iop_a_src = NULL;
   uint8_t* io0_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
   uint8_t* io1_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
-  if (a_src.private_impl.buf) {
-    iop_a_src =
-        a_src.private_impl.buf->data.ptr + a_src.private_impl.buf->meta.ri;
-    if (!a_src.private_impl.mark) {
-      a_src.private_impl.mark = iop_a_src;
-      a_src.private_impl.limit =
-          a_src.private_impl.buf->data.ptr + a_src.private_impl.buf->meta.wi;
-    }
-    io0_a_src = a_src.private_impl.mark;
-    io1_a_src = a_src.private_impl.limit;
+  uint8_t* io2_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
+  if (a_src) {
+    io0_a_src = a_src->data.ptr;
+    io1_a_src = io0_a_src + a_src->meta.ri;
+    iop_a_src = io1_a_src;
+    io2_a_src = io0_a_src + a_src->meta.wi;
   }
 
   uint32_t coro_susp_point = self->private_impl.p_skip_frame[0];
@@ -8875,7 +9295,7 @@
 
     {
       WUFFS_BASE__COROUTINE_SUSPENSION_POINT(1);
-      if (WUFFS_BASE__UNLIKELY(iop_a_src == io1_a_src)) {
+      if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
         status = wuffs_base__suspension__short_read;
         goto suspend;
       }
@@ -8887,36 +9307,45 @@
           (((uint32_t)(3)) << (1 + (v_flags & 7)));
       WUFFS_BASE__COROUTINE_SUSPENSION_POINT(2);
       if (self->private_data.s_skip_frame[0].scratch >
-          ((uint64_t)(io1_a_src - iop_a_src))) {
+          ((uint64_t)(io2_a_src - iop_a_src))) {
         self->private_data.s_skip_frame[0].scratch -=
-            ((uint64_t)(io1_a_src - iop_a_src));
-        iop_a_src = io1_a_src;
+            ((uint64_t)(io2_a_src - iop_a_src));
+        iop_a_src = io2_a_src;
         status = wuffs_base__suspension__short_read;
         goto suspend;
       }
       iop_a_src += self->private_data.s_skip_frame[0].scratch;
     }
-    WUFFS_BASE__COROUTINE_SUSPENSION_POINT(3);
-    if (WUFFS_BASE__UNLIKELY(iop_a_src == io1_a_src)) {
-      status = wuffs_base__suspension__short_read;
-      goto suspend;
+    {
+      WUFFS_BASE__COROUTINE_SUSPENSION_POINT(3);
+      if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
+        status = wuffs_base__suspension__short_read;
+        goto suspend;
+      }
+      uint8_t t_1 = *iop_a_src++;
+      v_lw = t_1;
     }
-    iop_a_src++;
-    if (a_src.private_impl.buf) {
-      a_src.private_impl.buf->meta.ri =
-          ((size_t)(iop_a_src - a_src.private_impl.buf->data.ptr));
+    if (v_lw > 8) {
+      status = wuffs_gif__error__bad_literal_width;
+      goto exit;
+    }
+    if (a_src) {
+      a_src->meta.ri = ((size_t)(iop_a_src - a_src->data.ptr));
     }
     WUFFS_BASE__COROUTINE_SUSPENSION_POINT(4);
     status = wuffs_gif__decoder__skip_blocks(self, a_src);
-    if (a_src.private_impl.buf) {
-      iop_a_src =
-          a_src.private_impl.buf->data.ptr + a_src.private_impl.buf->meta.ri;
+    if (a_src) {
+      iop_a_src = a_src->data.ptr + a_src->meta.ri;
     }
     if (status) {
       goto suspend;
     }
-    wuffs_base__u64__sat_add_indirect(
-        &self->private_impl.f_num_decoded_frames_value, 1);
+    if (self->private_impl.f_quirk_enabled_delay_num_decoded_frames) {
+      self->private_impl.f_delayed_num_decoded_frames = true;
+    } else {
+      wuffs_base__u64__sat_add_indirect(
+          &self->private_impl.f_num_decoded_frames_value, 1);
+    }
     wuffs_gif__decoder__reset_gc(self);
 
     goto ok;
@@ -8927,13 +9356,13 @@
 
   goto suspend;
 suspend:
-  self->private_impl.p_skip_frame[0] = coro_susp_point;
+  self->private_impl.p_skip_frame[0] =
+      wuffs_base__status__is_suspension(status) ? coro_susp_point : 0;
 
   goto exit;
 exit:
-  if (a_src.private_impl.buf) {
-    a_src.private_impl.buf->meta.ri =
-        ((size_t)(iop_a_src - a_src.private_impl.buf->data.ptr));
+  if (a_src) {
+    a_src->meta.ri = ((size_t)(iop_a_src - a_src->data.ptr));
   }
 
   return status;
@@ -8944,7 +9373,7 @@
 WUFFS_BASE__MAYBE_STATIC wuffs_base__status  //
 wuffs_gif__decoder__decode_frame(wuffs_gif__decoder* self,
                                  wuffs_base__pixel_buffer* a_dst,
-                                 wuffs_base__io_reader a_src,
+                                 wuffs_base__io_buffer* a_src,
                                  wuffs_base__slice_u8 a_workbuf,
                                  wuffs_base__decode_frame_options* a_opts) {
   if (!self) {
@@ -8955,12 +9384,12 @@
                ? wuffs_base__error__disabled_by_previous_error
                : wuffs_base__error__initialize_not_called;
   }
-  if (!a_dst) {
+  if (!a_dst || !a_src) {
     self->private_impl.magic = WUFFS_BASE__DISABLED;
     return wuffs_base__error__bad_argument;
   }
   if ((self->private_impl.active_coroutine != 0) &&
-      (self->private_impl.active_coroutine != 3)) {
+      (self->private_impl.active_coroutine != 4)) {
     self->private_impl.magic = WUFFS_BASE__DISABLED;
     return wuffs_base__error__interleaved_coroutine_calls;
   }
@@ -8973,13 +9402,22 @@
   switch (coro_susp_point) {
     WUFFS_BASE__COROUTINE_SUSPENSION_POINT_0;
 
-    if (self->private_impl.f_call_sequence != 2) {
+    self->private_impl.f_ignore_metadata = true;
+    if (self->private_impl.f_call_sequence != 4) {
       WUFFS_BASE__COROUTINE_SUSPENSION_POINT(1);
       status = wuffs_gif__decoder__decode_frame_config(self, NULL, a_src);
       if (status) {
         goto suspend;
       }
     }
+    if (self->private_impl.f_quirk_enabled_reject_empty_frame &&
+        ((self->private_impl.f_frame_rect_x0 ==
+          self->private_impl.f_frame_rect_x1) ||
+         (self->private_impl.f_frame_rect_y0 ==
+          self->private_impl.f_frame_rect_y1))) {
+      status = wuffs_gif__error__bad_frame_size;
+      goto exit;
+    }
     WUFFS_BASE__COROUTINE_SUSPENSION_POINT(2);
     status = wuffs_gif__decoder__decode_id_part1(self, a_dst, a_src);
     if (status) {
@@ -9002,8 +9440,10 @@
 
   goto suspend;
 suspend:
-  self->private_impl.p_decode_frame[0] = coro_susp_point;
-  self->private_impl.active_coroutine = 3;
+  self->private_impl.p_decode_frame[0] =
+      wuffs_base__status__is_suspension(status) ? coro_susp_point : 0;
+  self->private_impl.active_coroutine =
+      wuffs_base__status__is_suspension(status) ? 4 : 0;
 
   goto exit;
 exit:
@@ -9017,8 +9457,7 @@
 
 static wuffs_base__empty_struct  //
 wuffs_gif__decoder__reset_gc(wuffs_gif__decoder* self) {
-  self->private_impl.f_call_sequence = 3;
-  self->private_impl.f_seen_graphic_control = false;
+  self->private_impl.f_call_sequence = 5;
   self->private_impl.f_gc_has_transparent_index = false;
   self->private_impl.f_gc_transparent_index = 0;
   self->private_impl.f_gc_disposal = 0;
@@ -9030,7 +9469,7 @@
 
 static wuffs_base__status  //
 wuffs_gif__decoder__decode_up_to_id_part1(wuffs_gif__decoder* self,
-                                          wuffs_base__io_reader a_src) {
+                                          wuffs_base__io_buffer* a_src) {
   wuffs_base__status status = NULL;
 
   uint8_t v_block_type = 0;
@@ -9038,16 +9477,12 @@
   uint8_t* iop_a_src = NULL;
   uint8_t* io0_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
   uint8_t* io1_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
-  if (a_src.private_impl.buf) {
-    iop_a_src =
-        a_src.private_impl.buf->data.ptr + a_src.private_impl.buf->meta.ri;
-    if (!a_src.private_impl.mark) {
-      a_src.private_impl.mark = iop_a_src;
-      a_src.private_impl.limit =
-          a_src.private_impl.buf->data.ptr + a_src.private_impl.buf->meta.wi;
-    }
-    io0_a_src = a_src.private_impl.mark;
-    io1_a_src = a_src.private_impl.limit;
+  uint8_t* io2_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
+  if (a_src) {
+    io0_a_src = a_src->data.ptr;
+    io1_a_src = io0_a_src + a_src->meta.ri;
+    iop_a_src = io1_a_src;
+    io2_a_src = io0_a_src + a_src->meta.wi;
   }
 
   uint32_t coro_susp_point = self->private_impl.p_decode_up_to_id_part1[0];
@@ -9057,19 +9492,18 @@
     WUFFS_BASE__COROUTINE_SUSPENSION_POINT_0;
 
     if (!self->private_impl.f_restarted) {
-      self->private_impl.f_frame_config_io_position =
-          (a_src.private_impl.buf
-               ? wuffs_base__u64__sat_add(
-                     a_src.private_impl.buf->meta.pos,
-                     ((uint64_t)(iop_a_src - a_src.private_impl.buf->data.ptr)))
-               : 0);
+      if (self->private_impl.f_call_sequence != 2) {
+        self->private_impl.f_frame_config_io_position =
+            (a_src ? wuffs_base__u64__sat_add(
+                         a_src->meta.pos,
+                         ((uint64_t)(iop_a_src - a_src->data.ptr)))
+                   : 0);
+      }
     } else if (self->private_impl.f_frame_config_io_position !=
-               (a_src.private_impl.buf
-                    ? wuffs_base__u64__sat_add(
-                          a_src.private_impl.buf->meta.pos,
-                          ((uint64_t)(iop_a_src -
-                                      a_src.private_impl.buf->data.ptr)))
-                    : 0)) {
+               (a_src ? wuffs_base__u64__sat_add(
+                            a_src->meta.pos,
+                            ((uint64_t)(iop_a_src - a_src->data.ptr)))
+                      : 0)) {
       status = wuffs_base__error__bad_restart;
       goto exit;
     } else {
@@ -9078,7 +9512,7 @@
     while (true) {
       {
         WUFFS_BASE__COROUTINE_SUSPENSION_POINT(1);
-        if (WUFFS_BASE__UNLIKELY(iop_a_src == io1_a_src)) {
+        if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
           status = wuffs_base__suspension__short_read;
           goto suspend;
         }
@@ -9086,35 +9520,41 @@
         v_block_type = t_0;
       }
       if (v_block_type == 33) {
-        if (a_src.private_impl.buf) {
-          a_src.private_impl.buf->meta.ri =
-              ((size_t)(iop_a_src - a_src.private_impl.buf->data.ptr));
+        if (a_src) {
+          a_src->meta.ri = ((size_t)(iop_a_src - a_src->data.ptr));
         }
         WUFFS_BASE__COROUTINE_SUSPENSION_POINT(2);
         status = wuffs_gif__decoder__decode_extension(self, a_src);
-        if (a_src.private_impl.buf) {
-          iop_a_src = a_src.private_impl.buf->data.ptr +
-                      a_src.private_impl.buf->meta.ri;
+        if (a_src) {
+          iop_a_src = a_src->data.ptr + a_src->meta.ri;
         }
         if (status) {
           goto suspend;
         }
       } else if (v_block_type == 44) {
-        if (a_src.private_impl.buf) {
-          a_src.private_impl.buf->meta.ri =
-              ((size_t)(iop_a_src - a_src.private_impl.buf->data.ptr));
+        if (self->private_impl.f_delayed_num_decoded_frames) {
+          self->private_impl.f_delayed_num_decoded_frames = false;
+          wuffs_base__u64__sat_add_indirect(
+              &self->private_impl.f_num_decoded_frames_value, 1);
+        }
+        if (a_src) {
+          a_src->meta.ri = ((size_t)(iop_a_src - a_src->data.ptr));
         }
         WUFFS_BASE__COROUTINE_SUSPENSION_POINT(3);
         status = wuffs_gif__decoder__decode_id_part0(self, a_src);
-        if (a_src.private_impl.buf) {
-          iop_a_src = a_src.private_impl.buf->data.ptr +
-                      a_src.private_impl.buf->meta.ri;
+        if (a_src) {
+          iop_a_src = a_src->data.ptr + a_src->meta.ri;
         }
         if (status) {
           goto suspend;
         }
         goto label_0_break;
       } else if (v_block_type == 59) {
+        if (self->private_impl.f_delayed_num_decoded_frames) {
+          self->private_impl.f_delayed_num_decoded_frames = false;
+          wuffs_base__u64__sat_add_indirect(
+              &self->private_impl.f_num_decoded_frames_value, 1);
+        }
         self->private_impl.f_end_of_data = true;
         goto label_0_break;
       } else {
@@ -9132,13 +9572,13 @@
 
   goto suspend;
 suspend:
-  self->private_impl.p_decode_up_to_id_part1[0] = coro_susp_point;
+  self->private_impl.p_decode_up_to_id_part1[0] =
+      wuffs_base__status__is_suspension(status) ? coro_susp_point : 0;
 
   goto exit;
 exit:
-  if (a_src.private_impl.buf) {
-    a_src.private_impl.buf->meta.ri =
-        ((size_t)(iop_a_src - a_src.private_impl.buf->data.ptr));
+  if (a_src) {
+    a_src->meta.ri = ((size_t)(iop_a_src - a_src->data.ptr));
   }
 
   return status;
@@ -9148,7 +9588,7 @@
 
 static wuffs_base__status  //
 wuffs_gif__decoder__decode_header(wuffs_gif__decoder* self,
-                                  wuffs_base__io_reader a_src) {
+                                  wuffs_base__io_buffer* a_src) {
   wuffs_base__status status = NULL;
 
   uint8_t v_c[6] = {0};
@@ -9157,16 +9597,12 @@
   uint8_t* iop_a_src = NULL;
   uint8_t* io0_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
   uint8_t* io1_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
-  if (a_src.private_impl.buf) {
-    iop_a_src =
-        a_src.private_impl.buf->data.ptr + a_src.private_impl.buf->meta.ri;
-    if (!a_src.private_impl.mark) {
-      a_src.private_impl.mark = iop_a_src;
-      a_src.private_impl.limit =
-          a_src.private_impl.buf->data.ptr + a_src.private_impl.buf->meta.wi;
-    }
-    io0_a_src = a_src.private_impl.mark;
-    io1_a_src = a_src.private_impl.limit;
+  uint8_t* io2_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
+  if (a_src) {
+    io0_a_src = a_src->data.ptr;
+    io1_a_src = io0_a_src + a_src->meta.ri;
+    iop_a_src = io1_a_src;
+    io2_a_src = io0_a_src + a_src->meta.wi;
   }
 
   uint32_t coro_susp_point = self->private_impl.p_decode_header[0];
@@ -9180,7 +9616,7 @@
     while (v_i < 6) {
       {
         WUFFS_BASE__COROUTINE_SUSPENSION_POINT(1);
-        if (WUFFS_BASE__UNLIKELY(iop_a_src == io1_a_src)) {
+        if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
           status = wuffs_base__suspension__short_read;
           goto suspend;
         }
@@ -9203,15 +9639,15 @@
 
   goto suspend;
 suspend:
-  self->private_impl.p_decode_header[0] = coro_susp_point;
+  self->private_impl.p_decode_header[0] =
+      wuffs_base__status__is_suspension(status) ? coro_susp_point : 0;
   memcpy(self->private_data.s_decode_header[0].v_c, v_c, sizeof(v_c));
   self->private_data.s_decode_header[0].v_i = v_i;
 
   goto exit;
 exit:
-  if (a_src.private_impl.buf) {
-    a_src.private_impl.buf->meta.ri =
-        ((size_t)(iop_a_src - a_src.private_impl.buf->data.ptr));
+  if (a_src) {
+    a_src->meta.ri = ((size_t)(iop_a_src - a_src->data.ptr));
   }
 
   return status;
@@ -9221,32 +9657,32 @@
 
 static wuffs_base__status  //
 wuffs_gif__decoder__decode_lsd(wuffs_gif__decoder* self,
-                               wuffs_base__io_reader a_src) {
+                               wuffs_base__io_buffer* a_src) {
   wuffs_base__status status = NULL;
 
   uint8_t v_flags = 0;
+  uint8_t v_background_color_index = 0;
   uint32_t v_num_palette_entries = 0;
   uint32_t v_i = 0;
+  uint32_t v_j = 0;
   uint32_t v_argb = 0;
 
   uint8_t* iop_a_src = NULL;
   uint8_t* io0_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
   uint8_t* io1_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
-  if (a_src.private_impl.buf) {
-    iop_a_src =
-        a_src.private_impl.buf->data.ptr + a_src.private_impl.buf->meta.ri;
-    if (!a_src.private_impl.mark) {
-      a_src.private_impl.mark = iop_a_src;
-      a_src.private_impl.limit =
-          a_src.private_impl.buf->data.ptr + a_src.private_impl.buf->meta.wi;
-    }
-    io0_a_src = a_src.private_impl.mark;
-    io1_a_src = a_src.private_impl.limit;
+  uint8_t* io2_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
+  if (a_src) {
+    io0_a_src = a_src->data.ptr;
+    io1_a_src = io0_a_src + a_src->meta.ri;
+    iop_a_src = io1_a_src;
+    io2_a_src = io0_a_src + a_src->meta.wi;
   }
 
   uint32_t coro_susp_point = self->private_impl.p_decode_lsd[0];
   if (coro_susp_point) {
     v_flags = self->private_data.s_decode_lsd[0].v_flags;
+    v_background_color_index =
+        self->private_data.s_decode_lsd[0].v_background_color_index;
     v_num_palette_entries =
         self->private_data.s_decode_lsd[0].v_num_palette_entries;
     v_i = self->private_data.s_decode_lsd[0].v_i;
@@ -9257,14 +9693,14 @@
     {
       WUFFS_BASE__COROUTINE_SUSPENSION_POINT(1);
       uint32_t t_0;
-      if (WUFFS_BASE__LIKELY(io1_a_src - iop_a_src >= 2)) {
+      if (WUFFS_BASE__LIKELY(io2_a_src - iop_a_src >= 2)) {
         t_0 = ((uint32_t)(wuffs_base__load_u16le(iop_a_src)));
         iop_a_src += 2;
       } else {
         self->private_data.s_decode_lsd[0].scratch = 0;
         WUFFS_BASE__COROUTINE_SUSPENSION_POINT(2);
         while (true) {
-          if (WUFFS_BASE__UNLIKELY(iop_a_src == io1_a_src)) {
+          if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
             status = wuffs_base__suspension__short_read;
             goto suspend;
           }
@@ -9286,14 +9722,14 @@
     {
       WUFFS_BASE__COROUTINE_SUSPENSION_POINT(3);
       uint32_t t_1;
-      if (WUFFS_BASE__LIKELY(io1_a_src - iop_a_src >= 2)) {
+      if (WUFFS_BASE__LIKELY(io2_a_src - iop_a_src >= 2)) {
         t_1 = ((uint32_t)(wuffs_base__load_u16le(iop_a_src)));
         iop_a_src += 2;
       } else {
         self->private_data.s_decode_lsd[0].scratch = 0;
         WUFFS_BASE__COROUTINE_SUSPENSION_POINT(4);
         while (true) {
-          if (WUFFS_BASE__UNLIKELY(iop_a_src == io1_a_src)) {
+          if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
             status = wuffs_base__suspension__short_read;
             goto suspend;
           }
@@ -9314,56 +9750,61 @@
     }
     {
       WUFFS_BASE__COROUTINE_SUSPENSION_POINT(5);
-      if (WUFFS_BASE__UNLIKELY(iop_a_src == io1_a_src)) {
+      if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
         status = wuffs_base__suspension__short_read;
         goto suspend;
       }
       uint8_t t_2 = *iop_a_src++;
       v_flags = t_2;
     }
-    self->private_data.s_decode_lsd[0].scratch = 2;
-    WUFFS_BASE__COROUTINE_SUSPENSION_POINT(6);
-    if (self->private_data.s_decode_lsd[0].scratch >
-        ((uint64_t)(io1_a_src - iop_a_src))) {
-      self->private_data.s_decode_lsd[0].scratch -=
-          ((uint64_t)(io1_a_src - iop_a_src));
-      iop_a_src = io1_a_src;
+    {
+      WUFFS_BASE__COROUTINE_SUSPENSION_POINT(6);
+      if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
+        status = wuffs_base__suspension__short_read;
+        goto suspend;
+      }
+      uint8_t t_3 = *iop_a_src++;
+      v_background_color_index = t_3;
+    }
+    WUFFS_BASE__COROUTINE_SUSPENSION_POINT(7);
+    if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
       status = wuffs_base__suspension__short_read;
       goto suspend;
     }
-    iop_a_src += self->private_data.s_decode_lsd[0].scratch;
-    if ((v_flags & 128) != 0) {
+    iop_a_src++;
+    v_i = 0;
+    self->private_impl.f_has_global_palette = ((v_flags & 128) != 0);
+    if (self->private_impl.f_has_global_palette) {
       v_num_palette_entries = (((uint32_t)(1)) << (1 + (v_flags & 7)));
-      v_i = 0;
       while (v_i < v_num_palette_entries) {
         {
-          WUFFS_BASE__COROUTINE_SUSPENSION_POINT(7);
-          uint32_t t_3;
-          if (WUFFS_BASE__LIKELY(io1_a_src - iop_a_src >= 3)) {
-            t_3 = ((uint32_t)(wuffs_base__load_u24be(iop_a_src)));
+          WUFFS_BASE__COROUTINE_SUSPENSION_POINT(8);
+          uint32_t t_4;
+          if (WUFFS_BASE__LIKELY(io2_a_src - iop_a_src >= 3)) {
+            t_4 = ((uint32_t)(wuffs_base__load_u24be(iop_a_src)));
             iop_a_src += 3;
           } else {
             self->private_data.s_decode_lsd[0].scratch = 0;
-            WUFFS_BASE__COROUTINE_SUSPENSION_POINT(8);
+            WUFFS_BASE__COROUTINE_SUSPENSION_POINT(9);
             while (true) {
-              if (WUFFS_BASE__UNLIKELY(iop_a_src == io1_a_src)) {
+              if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
                 status = wuffs_base__suspension__short_read;
                 goto suspend;
               }
               uint64_t* scratch = &self->private_data.s_decode_lsd[0].scratch;
-              uint32_t num_bits_3 = ((uint32_t)(*scratch & 0xFF));
+              uint32_t num_bits_4 = ((uint32_t)(*scratch & 0xFF));
               *scratch >>= 8;
               *scratch <<= 8;
-              *scratch |= ((uint64_t)(*iop_a_src++)) << (56 - num_bits_3);
-              if (num_bits_3 == 16) {
-                t_3 = ((uint32_t)(*scratch >> 40));
+              *scratch |= ((uint64_t)(*iop_a_src++)) << (56 - num_bits_4);
+              if (num_bits_4 == 16) {
+                t_4 = ((uint32_t)(*scratch >> 40));
                 break;
               }
-              num_bits_3 += 8;
-              *scratch |= ((uint64_t)(num_bits_3));
+              num_bits_4 += 8;
+              *scratch |= ((uint64_t)(num_bits_4));
             }
           }
-          v_argb = t_3;
+          v_argb = t_4;
         }
         v_argb |= 4278190080;
         self->private_data.f_palettes[0][((4 * v_i) + 0)] =
@@ -9376,14 +9817,31 @@
             ((uint8_t)(((v_argb >> 24) & 255)));
         v_i += 1;
       }
-      while (v_i < 256) {
-        self->private_data.f_palettes[0][((4 * v_i) + 0)] = 0;
-        self->private_data.f_palettes[0][((4 * v_i) + 1)] = 0;
-        self->private_data.f_palettes[0][((4 * v_i) + 2)] = 0;
-        self->private_data.f_palettes[0][((4 * v_i) + 3)] = 255;
-        v_i += 1;
+      if (self->private_impl.f_quirk_enabled_honor_background_color) {
+        if ((v_background_color_index != 0) &&
+            (((uint32_t)(v_background_color_index)) < v_num_palette_entries)) {
+          v_j = (4 * ((uint32_t)(v_background_color_index)));
+          self->private_impl.f_background_color_u32_argb_premul =
+              ((((uint32_t)(self->private_data.f_palettes[0][(v_j + 0)]))
+                << 0) |
+               (((uint32_t)(self->private_data.f_palettes[0][(v_j + 1)]))
+                << 8) |
+               (((uint32_t)(self->private_data.f_palettes[0][(v_j + 2)]))
+                << 16) |
+               (((uint32_t)(self->private_data.f_palettes[0][(v_j + 3)]))
+                << 24));
+        } else {
+          self->private_impl.f_background_color_u32_argb_premul = 77;
+        }
       }
     }
+    while (v_i < 256) {
+      self->private_data.f_palettes[0][((4 * v_i) + 0)] = 0;
+      self->private_data.f_palettes[0][((4 * v_i) + 1)] = 0;
+      self->private_data.f_palettes[0][((4 * v_i) + 2)] = 0;
+      self->private_data.f_palettes[0][((4 * v_i) + 3)] = 255;
+      v_i += 1;
+    }
 
     goto ok;
   ok:
@@ -9393,17 +9851,19 @@
 
   goto suspend;
 suspend:
-  self->private_impl.p_decode_lsd[0] = coro_susp_point;
+  self->private_impl.p_decode_lsd[0] =
+      wuffs_base__status__is_suspension(status) ? coro_susp_point : 0;
   self->private_data.s_decode_lsd[0].v_flags = v_flags;
+  self->private_data.s_decode_lsd[0].v_background_color_index =
+      v_background_color_index;
   self->private_data.s_decode_lsd[0].v_num_palette_entries =
       v_num_palette_entries;
   self->private_data.s_decode_lsd[0].v_i = v_i;
 
   goto exit;
 exit:
-  if (a_src.private_impl.buf) {
-    a_src.private_impl.buf->meta.ri =
-        ((size_t)(iop_a_src - a_src.private_impl.buf->data.ptr));
+  if (a_src) {
+    a_src->meta.ri = ((size_t)(iop_a_src - a_src->data.ptr));
   }
 
   return status;
@@ -9413,7 +9873,7 @@
 
 static wuffs_base__status  //
 wuffs_gif__decoder__decode_extension(wuffs_gif__decoder* self,
-                                     wuffs_base__io_reader a_src) {
+                                     wuffs_base__io_buffer* a_src) {
   wuffs_base__status status = NULL;
 
   uint8_t v_label = 0;
@@ -9421,16 +9881,12 @@
   uint8_t* iop_a_src = NULL;
   uint8_t* io0_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
   uint8_t* io1_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
-  if (a_src.private_impl.buf) {
-    iop_a_src =
-        a_src.private_impl.buf->data.ptr + a_src.private_impl.buf->meta.ri;
-    if (!a_src.private_impl.mark) {
-      a_src.private_impl.mark = iop_a_src;
-      a_src.private_impl.limit =
-          a_src.private_impl.buf->data.ptr + a_src.private_impl.buf->meta.wi;
-    }
-    io0_a_src = a_src.private_impl.mark;
-    io1_a_src = a_src.private_impl.limit;
+  uint8_t* io2_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
+  if (a_src) {
+    io0_a_src = a_src->data.ptr;
+    io1_a_src = io0_a_src + a_src->meta.ri;
+    iop_a_src = io1_a_src;
+    io2_a_src = io0_a_src + a_src->meta.wi;
   }
 
   uint32_t coro_susp_point = self->private_impl.p_decode_extension[0];
@@ -9441,7 +9897,7 @@
 
     {
       WUFFS_BASE__COROUTINE_SUSPENSION_POINT(1);
-      if (WUFFS_BASE__UNLIKELY(iop_a_src == io1_a_src)) {
+      if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
         status = wuffs_base__suspension__short_read;
         goto suspend;
       }
@@ -9449,15 +9905,13 @@
       v_label = t_0;
     }
     if (v_label == 249) {
-      if (a_src.private_impl.buf) {
-        a_src.private_impl.buf->meta.ri =
-            ((size_t)(iop_a_src - a_src.private_impl.buf->data.ptr));
+      if (a_src) {
+        a_src->meta.ri = ((size_t)(iop_a_src - a_src->data.ptr));
       }
       WUFFS_BASE__COROUTINE_SUSPENSION_POINT(2);
       status = wuffs_gif__decoder__decode_gc(self, a_src);
-      if (a_src.private_impl.buf) {
-        iop_a_src =
-            a_src.private_impl.buf->data.ptr + a_src.private_impl.buf->meta.ri;
+      if (a_src) {
+        iop_a_src = a_src->data.ptr + a_src->meta.ri;
       }
       if (status) {
         goto suspend;
@@ -9465,15 +9919,13 @@
       status = NULL;
       goto ok;
     } else if (v_label == 255) {
-      if (a_src.private_impl.buf) {
-        a_src.private_impl.buf->meta.ri =
-            ((size_t)(iop_a_src - a_src.private_impl.buf->data.ptr));
+      if (a_src) {
+        a_src->meta.ri = ((size_t)(iop_a_src - a_src->data.ptr));
       }
       WUFFS_BASE__COROUTINE_SUSPENSION_POINT(3);
       status = wuffs_gif__decoder__decode_ae(self, a_src);
-      if (a_src.private_impl.buf) {
-        iop_a_src =
-            a_src.private_impl.buf->data.ptr + a_src.private_impl.buf->meta.ri;
+      if (a_src) {
+        iop_a_src = a_src->data.ptr + a_src->meta.ri;
       }
       if (status) {
         goto suspend;
@@ -9481,15 +9933,13 @@
       status = NULL;
       goto ok;
     }
-    if (a_src.private_impl.buf) {
-      a_src.private_impl.buf->meta.ri =
-          ((size_t)(iop_a_src - a_src.private_impl.buf->data.ptr));
+    if (a_src) {
+      a_src->meta.ri = ((size_t)(iop_a_src - a_src->data.ptr));
     }
     WUFFS_BASE__COROUTINE_SUSPENSION_POINT(4);
     status = wuffs_gif__decoder__skip_blocks(self, a_src);
-    if (a_src.private_impl.buf) {
-      iop_a_src =
-          a_src.private_impl.buf->data.ptr + a_src.private_impl.buf->meta.ri;
+    if (a_src) {
+      iop_a_src = a_src->data.ptr + a_src->meta.ri;
     }
     if (status) {
       goto suspend;
@@ -9503,13 +9953,13 @@
 
   goto suspend;
 suspend:
-  self->private_impl.p_decode_extension[0] = coro_susp_point;
+  self->private_impl.p_decode_extension[0] =
+      wuffs_base__status__is_suspension(status) ? coro_susp_point : 0;
 
   goto exit;
 exit:
-  if (a_src.private_impl.buf) {
-    a_src.private_impl.buf->meta.ri =
-        ((size_t)(iop_a_src - a_src.private_impl.buf->data.ptr));
+  if (a_src) {
+    a_src->meta.ri = ((size_t)(iop_a_src - a_src->data.ptr));
   }
 
   return status;
@@ -9519,7 +9969,7 @@
 
 static wuffs_base__status  //
 wuffs_gif__decoder__skip_blocks(wuffs_gif__decoder* self,
-                                wuffs_base__io_reader a_src) {
+                                wuffs_base__io_buffer* a_src) {
   wuffs_base__status status = NULL;
 
   uint8_t v_block_size = 0;
@@ -9527,16 +9977,12 @@
   uint8_t* iop_a_src = NULL;
   uint8_t* io0_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
   uint8_t* io1_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
-  if (a_src.private_impl.buf) {
-    iop_a_src =
-        a_src.private_impl.buf->data.ptr + a_src.private_impl.buf->meta.ri;
-    if (!a_src.private_impl.mark) {
-      a_src.private_impl.mark = iop_a_src;
-      a_src.private_impl.limit =
-          a_src.private_impl.buf->data.ptr + a_src.private_impl.buf->meta.wi;
-    }
-    io0_a_src = a_src.private_impl.mark;
-    io1_a_src = a_src.private_impl.limit;
+  uint8_t* io2_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
+  if (a_src) {
+    io0_a_src = a_src->data.ptr;
+    io1_a_src = io0_a_src + a_src->meta.ri;
+    iop_a_src = io1_a_src;
+    io2_a_src = io0_a_src + a_src->meta.wi;
   }
 
   uint32_t coro_susp_point = self->private_impl.p_skip_blocks[0];
@@ -9548,7 +9994,7 @@
     while (true) {
       {
         WUFFS_BASE__COROUTINE_SUSPENSION_POINT(1);
-        if (WUFFS_BASE__UNLIKELY(iop_a_src == io1_a_src)) {
+        if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
           status = wuffs_base__suspension__short_read;
           goto suspend;
         }
@@ -9562,10 +10008,10 @@
       self->private_data.s_skip_blocks[0].scratch = ((uint32_t)(v_block_size));
       WUFFS_BASE__COROUTINE_SUSPENSION_POINT(2);
       if (self->private_data.s_skip_blocks[0].scratch >
-          ((uint64_t)(io1_a_src - iop_a_src))) {
+          ((uint64_t)(io2_a_src - iop_a_src))) {
         self->private_data.s_skip_blocks[0].scratch -=
-            ((uint64_t)(io1_a_src - iop_a_src));
-        iop_a_src = io1_a_src;
+            ((uint64_t)(io2_a_src - iop_a_src));
+        iop_a_src = io2_a_src;
         status = wuffs_base__suspension__short_read;
         goto suspend;
       }
@@ -9580,13 +10026,13 @@
 
   goto suspend;
 suspend:
-  self->private_impl.p_skip_blocks[0] = coro_susp_point;
+  self->private_impl.p_skip_blocks[0] =
+      wuffs_base__status__is_suspension(status) ? coro_susp_point : 0;
 
   goto exit;
 exit:
-  if (a_src.private_impl.buf) {
-    a_src.private_impl.buf->meta.ri =
-        ((size_t)(iop_a_src - a_src.private_impl.buf->data.ptr));
+  if (a_src) {
+    a_src->meta.ri = ((size_t)(iop_a_src - a_src->data.ptr));
   }
 
   return status;
@@ -9596,34 +10042,34 @@
 
 static wuffs_base__status  //
 wuffs_gif__decoder__decode_ae(wuffs_gif__decoder* self,
-                              wuffs_base__io_reader a_src) {
+                              wuffs_base__io_buffer* a_src) {
   wuffs_base__status status = NULL;
 
   uint8_t v_c = 0;
   uint8_t v_block_size = 0;
-  bool v_not_animexts = false;
-  bool v_not_netscape = false;
+  bool v_is_animexts = false;
+  bool v_is_netscape = false;
+  bool v_is_iccp = false;
+  bool v_is_xmp = false;
 
   uint8_t* iop_a_src = NULL;
   uint8_t* io0_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
   uint8_t* io1_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
-  if (a_src.private_impl.buf) {
-    iop_a_src =
-        a_src.private_impl.buf->data.ptr + a_src.private_impl.buf->meta.ri;
-    if (!a_src.private_impl.mark) {
-      a_src.private_impl.mark = iop_a_src;
-      a_src.private_impl.limit =
-          a_src.private_impl.buf->data.ptr + a_src.private_impl.buf->meta.wi;
-    }
-    io0_a_src = a_src.private_impl.mark;
-    io1_a_src = a_src.private_impl.limit;
+  uint8_t* io2_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
+  if (a_src) {
+    io0_a_src = a_src->data.ptr;
+    io1_a_src = io0_a_src + a_src->meta.ri;
+    iop_a_src = io1_a_src;
+    io2_a_src = io0_a_src + a_src->meta.wi;
   }
 
   uint32_t coro_susp_point = self->private_impl.p_decode_ae[0];
   if (coro_susp_point) {
     v_block_size = self->private_data.s_decode_ae[0].v_block_size;
-    v_not_animexts = self->private_data.s_decode_ae[0].v_not_animexts;
-    v_not_netscape = self->private_data.s_decode_ae[0].v_not_netscape;
+    v_is_animexts = self->private_data.s_decode_ae[0].v_is_animexts;
+    v_is_netscape = self->private_data.s_decode_ae[0].v_is_netscape;
+    v_is_iccp = self->private_data.s_decode_ae[0].v_is_iccp;
+    v_is_xmp = self->private_data.s_decode_ae[0].v_is_xmp;
   }
   switch (coro_susp_point) {
     WUFFS_BASE__COROUTINE_SUSPENSION_POINT_0;
@@ -9631,7 +10077,7 @@
     while (true) {
       {
         WUFFS_BASE__COROUTINE_SUSPENSION_POINT(1);
-        if (WUFFS_BASE__UNLIKELY(iop_a_src == io1_a_src)) {
+        if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
           status = wuffs_base__suspension__short_read;
           goto suspend;
         }
@@ -9646,33 +10092,38 @@
         self->private_data.s_decode_ae[0].scratch = ((uint32_t)(v_block_size));
         WUFFS_BASE__COROUTINE_SUSPENSION_POINT(2);
         if (self->private_data.s_decode_ae[0].scratch >
-            ((uint64_t)(io1_a_src - iop_a_src))) {
+            ((uint64_t)(io2_a_src - iop_a_src))) {
           self->private_data.s_decode_ae[0].scratch -=
-              ((uint64_t)(io1_a_src - iop_a_src));
-          iop_a_src = io1_a_src;
+              ((uint64_t)(io2_a_src - iop_a_src));
+          iop_a_src = io2_a_src;
           status = wuffs_base__suspension__short_read;
           goto suspend;
         }
         iop_a_src += self->private_data.s_decode_ae[0].scratch;
         goto label_0_break;
       }
-      v_not_animexts = false;
-      v_not_netscape = false;
+      v_is_animexts = true;
+      v_is_netscape = true;
+      v_is_iccp = true;
+      v_is_xmp = true;
       v_block_size = 0;
       while (v_block_size < 11) {
         {
           WUFFS_BASE__COROUTINE_SUSPENSION_POINT(3);
-          if (WUFFS_BASE__UNLIKELY(iop_a_src == io1_a_src)) {
+          if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
             status = wuffs_base__suspension__short_read;
             goto suspend;
           }
           uint8_t t_1 = *iop_a_src++;
           v_c = t_1;
         }
-        v_not_animexts =
-            (v_not_animexts || (v_c != wuffs_gif__animexts1dot0[v_block_size]));
-        v_not_netscape =
-            (v_not_netscape || (v_c != wuffs_gif__netscape2dot0[v_block_size]));
+        v_is_animexts =
+            (v_is_animexts && (v_c == wuffs_gif__animexts1dot0[v_block_size]));
+        v_is_netscape =
+            (v_is_netscape && (v_c == wuffs_gif__netscape2dot0[v_block_size]));
+        v_is_iccp =
+            (v_is_iccp && (v_c == wuffs_gif__iccrgbg1012[v_block_size]));
+        v_is_xmp = (v_is_xmp && (v_c == wuffs_gif__xmpdataxmp[v_block_size]));
 #if defined(__GNUC__)
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wconversion"
@@ -9682,101 +10133,135 @@
 #pragma GCC diagnostic pop
 #endif
       }
-      if (v_not_animexts && v_not_netscape) {
-        goto label_0_break;
-      }
-      {
-        WUFFS_BASE__COROUTINE_SUSPENSION_POINT(4);
-        if (WUFFS_BASE__UNLIKELY(iop_a_src == io1_a_src)) {
-          status = wuffs_base__suspension__short_read;
-          goto suspend;
-        }
-        uint8_t t_2 = *iop_a_src++;
-        v_block_size = t_2;
-      }
-      if (v_block_size != 3) {
-        self->private_data.s_decode_ae[0].scratch = ((uint32_t)(v_block_size));
-        WUFFS_BASE__COROUTINE_SUSPENSION_POINT(5);
-        if (self->private_data.s_decode_ae[0].scratch >
-            ((uint64_t)(io1_a_src - iop_a_src))) {
-          self->private_data.s_decode_ae[0].scratch -=
-              ((uint64_t)(io1_a_src - iop_a_src));
-          iop_a_src = io1_a_src;
-          status = wuffs_base__suspension__short_read;
-          goto suspend;
-        }
-        iop_a_src += self->private_data.s_decode_ae[0].scratch;
-        goto label_0_break;
-      }
-      {
-        WUFFS_BASE__COROUTINE_SUSPENSION_POINT(6);
-        if (WUFFS_BASE__UNLIKELY(iop_a_src == io1_a_src)) {
-          status = wuffs_base__suspension__short_read;
-          goto suspend;
-        }
-        uint8_t t_3 = *iop_a_src++;
-        v_c = t_3;
-      }
-      if (v_c != 1) {
-        self->private_data.s_decode_ae[0].scratch = 2;
-        WUFFS_BASE__COROUTINE_SUSPENSION_POINT(7);
-        if (self->private_data.s_decode_ae[0].scratch >
-            ((uint64_t)(io1_a_src - iop_a_src))) {
-          self->private_data.s_decode_ae[0].scratch -=
-              ((uint64_t)(io1_a_src - iop_a_src));
-          iop_a_src = io1_a_src;
-          status = wuffs_base__suspension__short_read;
-          goto suspend;
-        }
-        iop_a_src += self->private_data.s_decode_ae[0].scratch;
-        goto label_0_break;
-      }
-      {
-        WUFFS_BASE__COROUTINE_SUSPENSION_POINT(8);
-        uint32_t t_4;
-        if (WUFFS_BASE__LIKELY(io1_a_src - iop_a_src >= 2)) {
-          t_4 = ((uint32_t)(wuffs_base__load_u16le(iop_a_src)));
-          iop_a_src += 2;
-        } else {
-          self->private_data.s_decode_ae[0].scratch = 0;
-          WUFFS_BASE__COROUTINE_SUSPENSION_POINT(9);
-          while (true) {
-            if (WUFFS_BASE__UNLIKELY(iop_a_src == io1_a_src)) {
-              status = wuffs_base__suspension__short_read;
-              goto suspend;
-            }
-            uint64_t* scratch = &self->private_data.s_decode_ae[0].scratch;
-            uint32_t num_bits_4 = ((uint32_t)(*scratch >> 56));
-            *scratch <<= 8;
-            *scratch >>= 8;
-            *scratch |= ((uint64_t)(*iop_a_src++)) << num_bits_4;
-            if (num_bits_4 == 8) {
-              t_4 = ((uint32_t)(*scratch));
-              break;
-            }
-            num_bits_4 += 8;
-            *scratch |= ((uint64_t)(num_bits_4)) << 56;
+      if (v_is_animexts || v_is_netscape) {
+        {
+          WUFFS_BASE__COROUTINE_SUSPENSION_POINT(4);
+          if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
+            status = wuffs_base__suspension__short_read;
+            goto suspend;
           }
+          uint8_t t_2 = *iop_a_src++;
+          v_block_size = t_2;
         }
-        self->private_impl.f_num_loops = t_4;
-      }
-      self->private_impl.f_seen_num_loops = true;
-      if ((0 < self->private_impl.f_num_loops) &&
-          (self->private_impl.f_num_loops <= 65535)) {
-        self->private_impl.f_num_loops += 1;
+        if (v_block_size != 3) {
+          self->private_data.s_decode_ae[0].scratch =
+              ((uint32_t)(v_block_size));
+          WUFFS_BASE__COROUTINE_SUSPENSION_POINT(5);
+          if (self->private_data.s_decode_ae[0].scratch >
+              ((uint64_t)(io2_a_src - iop_a_src))) {
+            self->private_data.s_decode_ae[0].scratch -=
+                ((uint64_t)(io2_a_src - iop_a_src));
+            iop_a_src = io2_a_src;
+            status = wuffs_base__suspension__short_read;
+            goto suspend;
+          }
+          iop_a_src += self->private_data.s_decode_ae[0].scratch;
+          goto label_0_break;
+        }
+        {
+          WUFFS_BASE__COROUTINE_SUSPENSION_POINT(6);
+          if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
+            status = wuffs_base__suspension__short_read;
+            goto suspend;
+          }
+          uint8_t t_3 = *iop_a_src++;
+          v_c = t_3;
+        }
+        if (v_c != 1) {
+          self->private_data.s_decode_ae[0].scratch = 2;
+          WUFFS_BASE__COROUTINE_SUSPENSION_POINT(7);
+          if (self->private_data.s_decode_ae[0].scratch >
+              ((uint64_t)(io2_a_src - iop_a_src))) {
+            self->private_data.s_decode_ae[0].scratch -=
+                ((uint64_t)(io2_a_src - iop_a_src));
+            iop_a_src = io2_a_src;
+            status = wuffs_base__suspension__short_read;
+            goto suspend;
+          }
+          iop_a_src += self->private_data.s_decode_ae[0].scratch;
+          goto label_0_break;
+        }
+        {
+          WUFFS_BASE__COROUTINE_SUSPENSION_POINT(8);
+          uint32_t t_4;
+          if (WUFFS_BASE__LIKELY(io2_a_src - iop_a_src >= 2)) {
+            t_4 = ((uint32_t)(wuffs_base__load_u16le(iop_a_src)));
+            iop_a_src += 2;
+          } else {
+            self->private_data.s_decode_ae[0].scratch = 0;
+            WUFFS_BASE__COROUTINE_SUSPENSION_POINT(9);
+            while (true) {
+              if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
+                status = wuffs_base__suspension__short_read;
+                goto suspend;
+              }
+              uint64_t* scratch = &self->private_data.s_decode_ae[0].scratch;
+              uint32_t num_bits_4 = ((uint32_t)(*scratch >> 56));
+              *scratch <<= 8;
+              *scratch >>= 8;
+              *scratch |= ((uint64_t)(*iop_a_src++)) << num_bits_4;
+              if (num_bits_4 == 8) {
+                t_4 = ((uint32_t)(*scratch));
+                break;
+              }
+              num_bits_4 += 8;
+              *scratch |= ((uint64_t)(num_bits_4)) << 56;
+            }
+          }
+          self->private_impl.f_num_loops = t_4;
+        }
+        self->private_impl.f_seen_num_loops = true;
+        if ((0 < self->private_impl.f_num_loops) &&
+            (self->private_impl.f_num_loops <= 65535)) {
+          self->private_impl.f_num_loops += 1;
+        }
+      } else if (self->private_impl.f_ignore_metadata) {
+      } else if (v_is_iccp && self->private_impl.f_report_metadata_iccp) {
+        while (((uint64_t)(io2_a_src - iop_a_src)) <= 0) {
+          status = wuffs_base__suspension__short_read;
+          WUFFS_BASE__COROUTINE_SUSPENSION_POINT_MAYBE_SUSPEND(10);
+        }
+        self->private_impl.f_metadata_chunk_length_value =
+            ((uint64_t)(wuffs_base__load_u8be(iop_a_src)));
+        (iop_a_src += 1, wuffs_base__make_empty_struct());
+        self->private_impl.f_metadata_fourcc_value = 1229144912;
+        self->private_impl.f_metadata_io_position = wuffs_base__u64__sat_add(
+            (a_src ? wuffs_base__u64__sat_add(
+                         a_src->meta.pos,
+                         ((uint64_t)(iop_a_src - a_src->data.ptr)))
+                   : 0),
+            self->private_impl.f_metadata_chunk_length_value);
+        self->private_impl.f_call_sequence = 1;
+        status = wuffs_base__warning__metadata_reported;
+        goto ok;
+      } else if (v_is_xmp && self->private_impl.f_report_metadata_xmp) {
+        while (((uint64_t)(io2_a_src - iop_a_src)) <= 0) {
+          status = wuffs_base__suspension__short_read;
+          WUFFS_BASE__COROUTINE_SUSPENSION_POINT_MAYBE_SUSPEND(11);
+        }
+        self->private_impl.f_metadata_chunk_length_value =
+            (((uint64_t)(wuffs_base__load_u8be(iop_a_src))) + 1);
+        self->private_impl.f_metadata_fourcc_value = 1481461792;
+        self->private_impl.f_metadata_io_position = wuffs_base__u64__sat_add(
+            (a_src ? wuffs_base__u64__sat_add(
+                         a_src->meta.pos,
+                         ((uint64_t)(iop_a_src - a_src->data.ptr)))
+                   : 0),
+            self->private_impl.f_metadata_chunk_length_value);
+        self->private_impl.f_call_sequence = 1;
+        status = wuffs_base__warning__metadata_reported;
+        goto ok;
       }
       goto label_0_break;
     }
   label_0_break:;
-    if (a_src.private_impl.buf) {
-      a_src.private_impl.buf->meta.ri =
-          ((size_t)(iop_a_src - a_src.private_impl.buf->data.ptr));
+    if (a_src) {
+      a_src->meta.ri = ((size_t)(iop_a_src - a_src->data.ptr));
     }
-    WUFFS_BASE__COROUTINE_SUSPENSION_POINT(10);
+    WUFFS_BASE__COROUTINE_SUSPENSION_POINT(12);
     status = wuffs_gif__decoder__skip_blocks(self, a_src);
-    if (a_src.private_impl.buf) {
-      iop_a_src =
-          a_src.private_impl.buf->data.ptr + a_src.private_impl.buf->meta.ri;
+    if (a_src) {
+      iop_a_src = a_src->data.ptr + a_src->meta.ri;
     }
     if (status) {
       goto suspend;
@@ -9790,16 +10275,18 @@
 
   goto suspend;
 suspend:
-  self->private_impl.p_decode_ae[0] = coro_susp_point;
+  self->private_impl.p_decode_ae[0] =
+      wuffs_base__status__is_suspension(status) ? coro_susp_point : 0;
   self->private_data.s_decode_ae[0].v_block_size = v_block_size;
-  self->private_data.s_decode_ae[0].v_not_animexts = v_not_animexts;
-  self->private_data.s_decode_ae[0].v_not_netscape = v_not_netscape;
+  self->private_data.s_decode_ae[0].v_is_animexts = v_is_animexts;
+  self->private_data.s_decode_ae[0].v_is_netscape = v_is_netscape;
+  self->private_data.s_decode_ae[0].v_is_iccp = v_is_iccp;
+  self->private_data.s_decode_ae[0].v_is_xmp = v_is_xmp;
 
   goto exit;
 exit:
-  if (a_src.private_impl.buf) {
-    a_src.private_impl.buf->meta.ri =
-        ((size_t)(iop_a_src - a_src.private_impl.buf->data.ptr));
+  if (a_src) {
+    a_src->meta.ri = ((size_t)(iop_a_src - a_src->data.ptr));
   }
 
   return status;
@@ -9809,7 +10296,7 @@
 
 static wuffs_base__status  //
 wuffs_gif__decoder__decode_gc(wuffs_gif__decoder* self,
-                              wuffs_base__io_reader a_src) {
+                              wuffs_base__io_buffer* a_src) {
   wuffs_base__status status = NULL;
 
   uint8_t v_c = 0;
@@ -9819,16 +10306,12 @@
   uint8_t* iop_a_src = NULL;
   uint8_t* io0_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
   uint8_t* io1_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
-  if (a_src.private_impl.buf) {
-    iop_a_src =
-        a_src.private_impl.buf->data.ptr + a_src.private_impl.buf->meta.ri;
-    if (!a_src.private_impl.mark) {
-      a_src.private_impl.mark = iop_a_src;
-      a_src.private_impl.limit =
-          a_src.private_impl.buf->data.ptr + a_src.private_impl.buf->meta.wi;
-    }
-    io0_a_src = a_src.private_impl.mark;
-    io1_a_src = a_src.private_impl.limit;
+  uint8_t* io2_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
+  if (a_src) {
+    io0_a_src = a_src->data.ptr;
+    io1_a_src = io0_a_src + a_src->meta.ri;
+    iop_a_src = io1_a_src;
+    io2_a_src = io0_a_src + a_src->meta.wi;
   }
 
   uint32_t coro_susp_point = self->private_impl.p_decode_gc[0];
@@ -9837,13 +10320,9 @@
   switch (coro_susp_point) {
     WUFFS_BASE__COROUTINE_SUSPENSION_POINT_0;
 
-    if (self->private_impl.f_seen_graphic_control) {
-      status = wuffs_gif__error__bad_graphic_control;
-      goto exit;
-    }
     {
       WUFFS_BASE__COROUTINE_SUSPENSION_POINT(1);
-      if (WUFFS_BASE__UNLIKELY(iop_a_src == io1_a_src)) {
+      if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
         status = wuffs_base__suspension__short_read;
         goto suspend;
       }
@@ -9856,7 +10335,7 @@
     }
     {
       WUFFS_BASE__COROUTINE_SUSPENSION_POINT(2);
-      if (WUFFS_BASE__UNLIKELY(iop_a_src == io1_a_src)) {
+      if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
         status = wuffs_base__suspension__short_read;
         goto suspend;
       }
@@ -9875,14 +10354,14 @@
     {
       WUFFS_BASE__COROUTINE_SUSPENSION_POINT(3);
       uint16_t t_2;
-      if (WUFFS_BASE__LIKELY(io1_a_src - iop_a_src >= 2)) {
+      if (WUFFS_BASE__LIKELY(io2_a_src - iop_a_src >= 2)) {
         t_2 = wuffs_base__load_u16le(iop_a_src);
         iop_a_src += 2;
       } else {
         self->private_data.s_decode_gc[0].scratch = 0;
         WUFFS_BASE__COROUTINE_SUSPENSION_POINT(4);
         while (true) {
-          if (WUFFS_BASE__UNLIKELY(iop_a_src == io1_a_src)) {
+          if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
             status = wuffs_base__suspension__short_read;
             goto suspend;
           }
@@ -9905,7 +10384,7 @@
         (((uint64_t)(v_gc_duration_centiseconds)) * 7056000);
     {
       WUFFS_BASE__COROUTINE_SUSPENSION_POINT(5);
-      if (WUFFS_BASE__UNLIKELY(iop_a_src == io1_a_src)) {
+      if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
         status = wuffs_base__suspension__short_read;
         goto suspend;
       }
@@ -9914,7 +10393,7 @@
     }
     {
       WUFFS_BASE__COROUTINE_SUSPENSION_POINT(6);
-      if (WUFFS_BASE__UNLIKELY(iop_a_src == io1_a_src)) {
+      if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
         status = wuffs_base__suspension__short_read;
         goto suspend;
       }
@@ -9925,7 +10404,6 @@
       status = wuffs_gif__error__bad_graphic_control;
       goto exit;
     }
-    self->private_impl.f_seen_graphic_control = true;
 
     goto ok;
   ok:
@@ -9935,13 +10413,13 @@
 
   goto suspend;
 suspend:
-  self->private_impl.p_decode_gc[0] = coro_susp_point;
+  self->private_impl.p_decode_gc[0] =
+      wuffs_base__status__is_suspension(status) ? coro_susp_point : 0;
 
   goto exit;
 exit:
-  if (a_src.private_impl.buf) {
-    a_src.private_impl.buf->meta.ri =
-        ((size_t)(iop_a_src - a_src.private_impl.buf->data.ptr));
+  if (a_src) {
+    a_src->meta.ri = ((size_t)(iop_a_src - a_src->data.ptr));
   }
 
   return status;
@@ -9951,22 +10429,18 @@
 
 static wuffs_base__status  //
 wuffs_gif__decoder__decode_id_part0(wuffs_gif__decoder* self,
-                                    wuffs_base__io_reader a_src) {
+                                    wuffs_base__io_buffer* a_src) {
   wuffs_base__status status = NULL;
 
   uint8_t* iop_a_src = NULL;
   uint8_t* io0_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
   uint8_t* io1_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
-  if (a_src.private_impl.buf) {
-    iop_a_src =
-        a_src.private_impl.buf->data.ptr + a_src.private_impl.buf->meta.ri;
-    if (!a_src.private_impl.mark) {
-      a_src.private_impl.mark = iop_a_src;
-      a_src.private_impl.limit =
-          a_src.private_impl.buf->data.ptr + a_src.private_impl.buf->meta.wi;
-    }
-    io0_a_src = a_src.private_impl.mark;
-    io1_a_src = a_src.private_impl.limit;
+  uint8_t* io2_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
+  if (a_src) {
+    io0_a_src = a_src->data.ptr;
+    io1_a_src = io0_a_src + a_src->meta.ri;
+    iop_a_src = io1_a_src;
+    io2_a_src = io0_a_src + a_src->meta.wi;
   }
 
   uint32_t coro_susp_point = self->private_impl.p_decode_id_part0[0];
@@ -9978,14 +10452,14 @@
     {
       WUFFS_BASE__COROUTINE_SUSPENSION_POINT(1);
       uint32_t t_0;
-      if (WUFFS_BASE__LIKELY(io1_a_src - iop_a_src >= 2)) {
+      if (WUFFS_BASE__LIKELY(io2_a_src - iop_a_src >= 2)) {
         t_0 = ((uint32_t)(wuffs_base__load_u16le(iop_a_src)));
         iop_a_src += 2;
       } else {
         self->private_data.s_decode_id_part0[0].scratch = 0;
         WUFFS_BASE__COROUTINE_SUSPENSION_POINT(2);
         while (true) {
-          if (WUFFS_BASE__UNLIKELY(iop_a_src == io1_a_src)) {
+          if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
             status = wuffs_base__suspension__short_read;
             goto suspend;
           }
@@ -10007,14 +10481,14 @@
     {
       WUFFS_BASE__COROUTINE_SUSPENSION_POINT(3);
       uint32_t t_1;
-      if (WUFFS_BASE__LIKELY(io1_a_src - iop_a_src >= 2)) {
+      if (WUFFS_BASE__LIKELY(io2_a_src - iop_a_src >= 2)) {
         t_1 = ((uint32_t)(wuffs_base__load_u16le(iop_a_src)));
         iop_a_src += 2;
       } else {
         self->private_data.s_decode_id_part0[0].scratch = 0;
         WUFFS_BASE__COROUTINE_SUSPENSION_POINT(4);
         while (true) {
-          if (WUFFS_BASE__UNLIKELY(iop_a_src == io1_a_src)) {
+          if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
             status = wuffs_base__suspension__short_read;
             goto suspend;
           }
@@ -10036,14 +10510,14 @@
     {
       WUFFS_BASE__COROUTINE_SUSPENSION_POINT(5);
       uint32_t t_2;
-      if (WUFFS_BASE__LIKELY(io1_a_src - iop_a_src >= 2)) {
+      if (WUFFS_BASE__LIKELY(io2_a_src - iop_a_src >= 2)) {
         t_2 = ((uint32_t)(wuffs_base__load_u16le(iop_a_src)));
         iop_a_src += 2;
       } else {
         self->private_data.s_decode_id_part0[0].scratch = 0;
         WUFFS_BASE__COROUTINE_SUSPENSION_POINT(6);
         while (true) {
-          if (WUFFS_BASE__UNLIKELY(iop_a_src == io1_a_src)) {
+          if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
             status = wuffs_base__suspension__short_read;
             goto suspend;
           }
@@ -10066,14 +10540,14 @@
     {
       WUFFS_BASE__COROUTINE_SUSPENSION_POINT(7);
       uint32_t t_3;
-      if (WUFFS_BASE__LIKELY(io1_a_src - iop_a_src >= 2)) {
+      if (WUFFS_BASE__LIKELY(io2_a_src - iop_a_src >= 2)) {
         t_3 = ((uint32_t)(wuffs_base__load_u16le(iop_a_src)));
         iop_a_src += 2;
       } else {
         self->private_data.s_decode_id_part0[0].scratch = 0;
         WUFFS_BASE__COROUTINE_SUSPENSION_POINT(8);
         while (true) {
-          if (WUFFS_BASE__UNLIKELY(iop_a_src == io1_a_src)) {
+          if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
             status = wuffs_base__suspension__short_read;
             goto suspend;
           }
@@ -10095,7 +10569,8 @@
     self->private_impl.f_frame_rect_y1 += self->private_impl.f_frame_rect_y0;
     self->private_impl.f_dst_x = self->private_impl.f_frame_rect_x0;
     self->private_impl.f_dst_y = self->private_impl.f_frame_rect_y0;
-    if (self->private_impl.f_call_sequence == 0) {
+    if ((self->private_impl.f_call_sequence == 0) &&
+        !self->private_impl.f_quirk_enabled_image_bounds_are_strict) {
       self->private_impl.f_width = wuffs_base__u32__max(
           self->private_impl.f_width, self->private_impl.f_frame_rect_x1);
       self->private_impl.f_height = wuffs_base__u32__max(
@@ -10110,13 +10585,13 @@
 
   goto suspend;
 suspend:
-  self->private_impl.p_decode_id_part0[0] = coro_susp_point;
+  self->private_impl.p_decode_id_part0[0] =
+      wuffs_base__status__is_suspension(status) ? coro_susp_point : 0;
 
   goto exit;
 exit:
-  if (a_src.private_impl.buf) {
-    a_src.private_impl.buf->meta.ri =
-        ((size_t)(iop_a_src - a_src.private_impl.buf->data.ptr));
+  if (a_src) {
+    a_src->meta.ri = ((size_t)(iop_a_src - a_src->data.ptr));
   }
 
   return status;
@@ -10127,33 +10602,32 @@
 static wuffs_base__status  //
 wuffs_gif__decoder__decode_id_part1(wuffs_gif__decoder* self,
                                     wuffs_base__pixel_buffer* a_dst,
-                                    wuffs_base__io_reader a_src) {
+                                    wuffs_base__io_buffer* a_src) {
   wuffs_base__status status = NULL;
 
   uint8_t v_flags = 0;
+  uint8_t v_which_palette = 0;
   uint32_t v_num_palette_entries = 0;
   uint32_t v_i = 0;
   uint32_t v_argb = 0;
   wuffs_base__slice_u8 v_dst_palette = {0};
+  wuffs_base__status v_status = NULL;
   uint8_t v_lw = 0;
 
   uint8_t* iop_a_src = NULL;
   uint8_t* io0_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
   uint8_t* io1_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
-  if (a_src.private_impl.buf) {
-    iop_a_src =
-        a_src.private_impl.buf->data.ptr + a_src.private_impl.buf->meta.ri;
-    if (!a_src.private_impl.mark) {
-      a_src.private_impl.mark = iop_a_src;
-      a_src.private_impl.limit =
-          a_src.private_impl.buf->data.ptr + a_src.private_impl.buf->meta.wi;
-    }
-    io0_a_src = a_src.private_impl.mark;
-    io1_a_src = a_src.private_impl.limit;
+  uint8_t* io2_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
+  if (a_src) {
+    io0_a_src = a_src->data.ptr;
+    io1_a_src = io0_a_src + a_src->meta.ri;
+    iop_a_src = io1_a_src;
+    io2_a_src = io0_a_src + a_src->meta.wi;
   }
 
   uint32_t coro_susp_point = self->private_impl.p_decode_id_part1[0];
   if (coro_susp_point) {
+    v_which_palette = self->private_data.s_decode_id_part1[0].v_which_palette;
     v_num_palette_entries =
         self->private_data.s_decode_id_part1[0].v_num_palette_entries;
     v_i = self->private_data.s_decode_id_part1[0].v_i;
@@ -10163,7 +10637,7 @@
 
     {
       WUFFS_BASE__COROUTINE_SUSPENSION_POINT(1);
-      if (WUFFS_BASE__UNLIKELY(iop_a_src == io1_a_src)) {
+      if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
         status = wuffs_base__suspension__short_read;
         goto suspend;
       }
@@ -10175,7 +10649,7 @@
     } else {
       self->private_impl.f_interlace = 0;
     }
-    self->private_impl.f_which_palette = 1;
+    v_which_palette = 1;
     if ((v_flags & 128) != 0) {
       v_num_palette_entries = (((uint32_t)(1)) << (1 + (v_flags & 7)));
       v_i = 0;
@@ -10183,14 +10657,14 @@
         {
           WUFFS_BASE__COROUTINE_SUSPENSION_POINT(2);
           uint32_t t_1;
-          if (WUFFS_BASE__LIKELY(io1_a_src - iop_a_src >= 3)) {
+          if (WUFFS_BASE__LIKELY(io2_a_src - iop_a_src >= 3)) {
             t_1 = ((uint32_t)(wuffs_base__load_u24be(iop_a_src)));
             iop_a_src += 3;
           } else {
             self->private_data.s_decode_id_part1[0].scratch = 0;
             WUFFS_BASE__COROUTINE_SUSPENSION_POINT(3);
             while (true) {
-              if (WUFFS_BASE__UNLIKELY(iop_a_src == io1_a_src)) {
+              if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
                 status = wuffs_base__suspension__short_read;
                 goto suspend;
               }
@@ -10228,12 +10702,16 @@
         self->private_data.f_palettes[1][((4 * v_i) + 3)] = 255;
         v_i += 1;
       }
+    } else if (self->private_impl.f_quirk_enabled_reject_empty_palette &&
+               !self->private_impl.f_has_global_palette) {
+      status = wuffs_gif__error__bad_palette;
+      goto exit;
     } else if (self->private_impl.f_gc_has_transparent_index) {
       wuffs_base__slice_u8__copy_from_slice(
           wuffs_base__make_slice_u8(self->private_data.f_palettes[1], 1024),
           wuffs_base__make_slice_u8(self->private_data.f_palettes[0], 1024));
     } else {
-      self->private_impl.f_which_palette = 0;
+      v_which_palette = 0;
     }
     if (self->private_impl.f_gc_has_transparent_index) {
       self->private_data.f_palettes[1][(
@@ -10254,13 +10732,22 @@
       v_dst_palette =
           wuffs_base__make_slice_u8(self->private_data.f_dst_palette, 1024);
     }
-    wuffs_base__pixel_swizzler__prepare(
+    v_status = wuffs_base__pixel_swizzler__prepare(
         &self->private_impl.f_swizzler,
         wuffs_base__pixel_buffer__pixel_format(a_dst), v_dst_palette,
         1191444488,
         wuffs_base__make_slice_u8(
-            self->private_data.f_palettes[self->private_impl.f_which_palette],
-            1024));
+            self->private_data.f_palettes[v_which_palette], 1024));
+    if (!wuffs_base__status__is_ok(v_status)) {
+      status = v_status;
+      if (wuffs_base__status__is_error(status)) {
+        goto exit;
+      } else if (wuffs_base__status__is_suspension(status)) {
+        status = wuffs_base__error__cannot_return_a_suspension;
+        goto exit;
+      }
+      goto ok;
+    }
     if (self->private_impl.f_previous_lzw_decode_ended_abruptly) {
       wuffs_base__ignore_status(wuffs_lzw__decoder__initialize(
           &self->private_data.f_lzw, sizeof(wuffs_lzw__decoder), WUFFS_VERSION,
@@ -10268,14 +10755,14 @@
     }
     {
       WUFFS_BASE__COROUTINE_SUSPENSION_POINT(4);
-      if (WUFFS_BASE__UNLIKELY(iop_a_src == io1_a_src)) {
+      if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
         status = wuffs_base__suspension__short_read;
         goto suspend;
       }
       uint8_t t_2 = *iop_a_src++;
       v_lw = t_2;
     }
-    if ((v_lw < 2) || (8 < v_lw)) {
+    if (v_lw > 8) {
       status = wuffs_gif__error__bad_literal_width;
       goto exit;
     }
@@ -10291,16 +10778,17 @@
 
   goto suspend;
 suspend:
-  self->private_impl.p_decode_id_part1[0] = coro_susp_point;
+  self->private_impl.p_decode_id_part1[0] =
+      wuffs_base__status__is_suspension(status) ? coro_susp_point : 0;
+  self->private_data.s_decode_id_part1[0].v_which_palette = v_which_palette;
   self->private_data.s_decode_id_part1[0].v_num_palette_entries =
       v_num_palette_entries;
   self->private_data.s_decode_id_part1[0].v_i = v_i;
 
   goto exit;
 exit:
-  if (a_src.private_impl.buf) {
-    a_src.private_impl.buf->meta.ri =
-        ((size_t)(iop_a_src - a_src.private_impl.buf->data.ptr));
+  if (a_src) {
+    a_src->meta.ri = ((size_t)(iop_a_src - a_src->data.ptr));
   }
 
   return status;
@@ -10311,7 +10799,7 @@
 static wuffs_base__status  //
 wuffs_gif__decoder__decode_id_part2(wuffs_gif__decoder* self,
                                     wuffs_base__pixel_buffer* a_dst,
-                                    wuffs_base__io_reader a_src,
+                                    wuffs_base__io_buffer* a_src,
                                     wuffs_base__slice_u8 a_workbuf) {
   wuffs_base__status status = NULL;
 
@@ -10319,11 +10807,13 @@
   bool v_need_block_size = false;
   uint64_t v_n_compressed = 0;
   wuffs_base__slice_u8 v_compressed = {0};
-  wuffs_base__io_reader v_r = wuffs_base__null_io_reader();
-  wuffs_base__io_buffer u_r WUFFS_BASE__POTENTIALLY_UNUSED =
-      wuffs_base__null_io_buffer();
+  wuffs_base__io_buffer u_r = wuffs_base__null_io_buffer();
+  wuffs_base__io_buffer* v_r = &u_r;
   uint8_t* iop_v_r WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
+  uint8_t* io0_v_r WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
   uint8_t* io1_v_r WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
+  uint8_t* io2_v_r WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
+  uint64_t v_mark = 0;
   wuffs_base__status v_lzw_status = NULL;
   wuffs_base__status v_copy_status = NULL;
   wuffs_base__slice_u8 v_uncompressed = {0};
@@ -10331,16 +10821,12 @@
   uint8_t* iop_a_src = NULL;
   uint8_t* io0_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
   uint8_t* io1_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
-  if (a_src.private_impl.buf) {
-    iop_a_src =
-        a_src.private_impl.buf->data.ptr + a_src.private_impl.buf->meta.ri;
-    if (!a_src.private_impl.mark) {
-      a_src.private_impl.mark = iop_a_src;
-      a_src.private_impl.limit =
-          a_src.private_impl.buf->data.ptr + a_src.private_impl.buf->meta.wi;
-    }
-    io0_a_src = a_src.private_impl.mark;
-    io1_a_src = a_src.private_impl.limit;
+  uint8_t* io2_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
+  if (a_src) {
+    io0_a_src = a_src->data.ptr;
+    io1_a_src = io0_a_src + a_src->meta.ri;
+    iop_a_src = io1_a_src;
+    io2_a_src = io0_a_src + a_src->meta.wi;
   }
 
   uint32_t coro_susp_point = self->private_impl.p_decode_id_part2[0];
@@ -10360,7 +10846,7 @@
         v_need_block_size = false;
         {
           WUFFS_BASE__COROUTINE_SUSPENSION_POINT(1);
-          if (WUFFS_BASE__UNLIKELY(iop_a_src == io1_a_src)) {
+          if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
             status = wuffs_base__suspension__short_read;
             goto suspend;
           }
@@ -10371,7 +10857,7 @@
       if (v_block_size == 0) {
         goto label_0_break;
       }
-      while (((uint64_t)(io1_a_src - iop_a_src)) == 0) {
+      while (((uint64_t)(io2_a_src - iop_a_src)) == 0) {
         status = wuffs_base__suspension__short_read;
         WUFFS_BASE__COROUTINE_SUSPENSION_POINT_MAYBE_SUSPEND(2);
       }
@@ -10382,12 +10868,12 @@
       }
       while (self->private_impl.f_compressed_wi <= 3841) {
         v_n_compressed = wuffs_base__u64__min(
-            v_block_size, ((uint64_t)(io1_a_src - iop_a_src)));
+            v_block_size, ((uint64_t)(io2_a_src - iop_a_src)));
         if (v_n_compressed <= 0) {
           goto label_1_break;
         }
         v_compressed =
-            wuffs_base__io_reader__take(&iop_a_src, io1_a_src, v_n_compressed);
+            wuffs_base__io_reader__take(&iop_a_src, io2_a_src, v_n_compressed);
         wuffs_base__slice_u8__copy_from_slice(
             wuffs_base__slice_u8__subslice_i(
                 wuffs_base__make_slice_u8(self->private_data.f_compressed,
@@ -10400,7 +10886,7 @@
         if (v_block_size > 0) {
           goto label_1_break;
         }
-        if (((uint64_t)(io1_a_src - iop_a_src)) <= 0) {
+        if (((uint64_t)(io2_a_src - iop_a_src)) <= 0) {
           v_need_block_size = true;
           goto label_1_break;
         }
@@ -10421,16 +10907,19 @@
           goto exit;
         }
         {
-          wuffs_base__io_reader o_0_v_r = v_r;
+          wuffs_base__io_buffer* o_0_v_r = v_r;
           uint8_t* o_0_iop_v_r = iop_v_r;
+          uint8_t* o_0_io0_v_r = io0_v_r;
           uint8_t* o_0_io1_v_r = io1_v_r;
-          wuffs_base__io_reader__set(
-              &v_r, &u_r, &iop_v_r, &io1_v_r,
+          uint8_t* o_0_io2_v_r = io2_v_r;
+          v_r = wuffs_base__io_reader__set(
+              &u_r, &iop_v_r, &io0_v_r, &io1_v_r, &io2_v_r,
               wuffs_base__slice_u8__subslice_ij(
                   wuffs_base__make_slice_u8(self->private_data.f_compressed,
                                             4096),
                   self->private_impl.f_compressed_ri,
                   self->private_impl.f_compressed_wi));
+          v_mark = ((uint64_t)(iop_v_r - io0_v_r));
           {
             u_r.meta.ri = ((size_t)(iop_v_r - u_r.data.ptr));
             wuffs_base__status t_1 = wuffs_lzw__decoder__decode_io_writer(
@@ -10442,10 +10931,13 @@
           }
           wuffs_base__u64__sat_add_indirect(
               &self->private_impl.f_compressed_ri,
-              ((uint64_t)(iop_v_r - v_r.private_impl.mark)));
+              wuffs_base__io__count_since(v_mark,
+                                          ((uint64_t)(iop_v_r - io0_v_r))));
           v_r = o_0_v_r;
           iop_v_r = o_0_iop_v_r;
+          io0_v_r = o_0_io0_v_r;
           io1_v_r = o_0_io1_v_r;
+          io2_v_r = o_0_io2_v_r;
         }
         v_uncompressed = wuffs_lzw__decoder__flush(&self->private_data.f_lzw);
         if (((uint64_t)(v_uncompressed.len)) > 0) {
@@ -10463,23 +10955,21 @@
                 ((uint32_t)(v_block_size));
             WUFFS_BASE__COROUTINE_SUSPENSION_POINT(3);
             if (self->private_data.s_decode_id_part2[0].scratch >
-                ((uint64_t)(io1_a_src - iop_a_src))) {
+                ((uint64_t)(io2_a_src - iop_a_src))) {
               self->private_data.s_decode_id_part2[0].scratch -=
-                  ((uint64_t)(io1_a_src - iop_a_src));
-              iop_a_src = io1_a_src;
+                  ((uint64_t)(io2_a_src - iop_a_src));
+              iop_a_src = io2_a_src;
               status = wuffs_base__suspension__short_read;
               goto suspend;
             }
             iop_a_src += self->private_data.s_decode_id_part2[0].scratch;
-            if (a_src.private_impl.buf) {
-              a_src.private_impl.buf->meta.ri =
-                  ((size_t)(iop_a_src - a_src.private_impl.buf->data.ptr));
+            if (a_src) {
+              a_src->meta.ri = ((size_t)(iop_a_src - a_src->data.ptr));
             }
             WUFFS_BASE__COROUTINE_SUSPENSION_POINT(4);
             status = wuffs_gif__decoder__skip_blocks(self, a_src);
-            if (a_src.private_impl.buf) {
-              iop_a_src = a_src.private_impl.buf->data.ptr +
-                          a_src.private_impl.buf->meta.ri;
+            if (a_src) {
+              iop_a_src = a_src->data.ptr + a_src->meta.ri;
             }
             if (status) {
               goto suspend;
@@ -10521,16 +11011,16 @@
 
   goto suspend;
 suspend:
-  self->private_impl.p_decode_id_part2[0] = coro_susp_point;
+  self->private_impl.p_decode_id_part2[0] =
+      wuffs_base__status__is_suspension(status) ? coro_susp_point : 0;
   self->private_data.s_decode_id_part2[0].v_block_size = v_block_size;
   self->private_data.s_decode_id_part2[0].v_need_block_size = v_need_block_size;
   self->private_data.s_decode_id_part2[0].v_lzw_status = v_lzw_status;
 
   goto exit;
 exit:
-  if (a_src.private_impl.buf) {
-    a_src.private_impl.buf->meta.ri =
-        ((size_t)(iop_a_src - a_src.private_impl.buf->data.ptr));
+  if (a_src) {
+    a_src->meta.ri = ((size_t)(iop_a_src - a_src->data.ptr));
   }
 
   return status;
@@ -10547,23 +11037,29 @@
   uint64_t v_n = 0;
   uint64_t v_src_ri = 0;
   uint32_t v_bytes_per_pixel = 0;
-  uint32_t v_pixfmt = 0;
+  uint32_t v_pixfmt_channels = 0;
   wuffs_base__table_u8 v_tab = {0};
   uint64_t v_i = 0;
   uint64_t v_j = 0;
 
-  v_bytes_per_pixel = 1;
-  v_pixfmt = wuffs_base__pixel_buffer__pixel_format(a_pb);
-  if ((v_pixfmt == 1157662856) || (v_pixfmt == 1426098312) ||
-      (v_pixfmt == 1174440072) || (v_pixfmt == 1442875528) ||
-      (v_pixfmt == 1191217288) || (v_pixfmt == 1459652744)) {
+  v_pixfmt_channels = (wuffs_base__pixel_buffer__pixel_format(a_pb) & 65535);
+  if (v_pixfmt_channels == 34952) {
     v_bytes_per_pixel = 4;
+  } else if (v_pixfmt_channels == 2184) {
+    v_bytes_per_pixel = 3;
+  } else if (v_pixfmt_channels == 8) {
+    v_bytes_per_pixel = 1;
+  } else {
+    return wuffs_base__error__unsupported_option;
   }
   v_tab = wuffs_base__pixel_buffer__plane(a_pb, 0);
 label_0_continue:;
   while (v_src_ri < ((uint64_t)(a_src.len))) {
     v_src = wuffs_base__slice_u8__subslice_i(a_src, v_src_ri);
     if (self->private_impl.f_dst_y >= self->private_impl.f_frame_rect_y1) {
+      if (self->private_impl.f_quirk_enabled_ignore_too_much_pixel_data) {
+        return NULL;
+      }
       return wuffs_base__error__too_much_data;
     }
     v_dst = wuffs_base__table_u8__row(v_tab, self->private_impl.f_dst_y);
@@ -10577,7 +11073,7 @@
       } else {
         v_dst = wuffs_base__slice_u8__subslice_i(v_dst, v_i);
       }
-      v_n = wuffs_base__pixel_swizzler__swizzle_packed(
+      v_n = wuffs_base__pixel_swizzler__swizzle_interleaved(
           &self->private_impl.f_swizzler, v_dst,
           wuffs_base__make_slice_u8(self->private_data.f_dst_palette, 1024),
           v_src);
@@ -10778,8 +11274,8 @@
 
 WUFFS_BASE__MAYBE_STATIC wuffs_base__status  //
 wuffs_gzip__decoder__decode_io_writer(wuffs_gzip__decoder* self,
-                                      wuffs_base__io_writer a_dst,
-                                      wuffs_base__io_reader a_src,
+                                      wuffs_base__io_buffer* a_dst,
+                                      wuffs_base__io_buffer* a_src,
                                       wuffs_base__slice_u8 a_workbuf) {
   if (!self) {
     return wuffs_base__error__bad_receiver;
@@ -10789,6 +11285,10 @@
                ? wuffs_base__error__disabled_by_previous_error
                : wuffs_base__error__initialize_not_called;
   }
+  if (!a_dst || !a_src) {
+    self->private_impl.magic = WUFFS_BASE__DISABLED;
+    return wuffs_base__error__bad_argument;
+  }
   if ((self->private_impl.active_coroutine != 0) &&
       (self->private_impl.active_coroutine != 1)) {
     self->private_impl.magic = WUFFS_BASE__DISABLED;
@@ -10800,6 +11300,7 @@
   uint8_t v_c = 0;
   uint8_t v_flags = 0;
   uint16_t v_xlen = 0;
+  uint64_t v_mark = 0;
   uint32_t v_checksum_got = 0;
   uint32_t v_decoded_length_got = 0;
   wuffs_base__status v_status = NULL;
@@ -10809,33 +11310,25 @@
   uint8_t* iop_a_dst = NULL;
   uint8_t* io0_a_dst WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
   uint8_t* io1_a_dst WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
-  if (a_dst.private_impl.buf) {
-    iop_a_dst =
-        a_dst.private_impl.buf->data.ptr + a_dst.private_impl.buf->meta.wi;
-    if (!a_dst.private_impl.mark) {
-      a_dst.private_impl.mark = iop_a_dst;
-      a_dst.private_impl.limit =
-          a_dst.private_impl.buf->data.ptr + a_dst.private_impl.buf->data.len;
+  uint8_t* io2_a_dst WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
+  if (a_dst) {
+    io0_a_dst = a_dst->data.ptr;
+    io1_a_dst = io0_a_dst + a_dst->meta.wi;
+    iop_a_dst = io1_a_dst;
+    io2_a_dst = io0_a_dst + a_dst->data.len;
+    if (a_dst->meta.closed) {
+      io2_a_dst = iop_a_dst;
     }
-    if (a_dst.private_impl.buf->meta.closed) {
-      a_dst.private_impl.limit = iop_a_dst;
-    }
-    io0_a_dst = a_dst.private_impl.mark;
-    io1_a_dst = a_dst.private_impl.limit;
   }
   uint8_t* iop_a_src = NULL;
   uint8_t* io0_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
   uint8_t* io1_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
-  if (a_src.private_impl.buf) {
-    iop_a_src =
-        a_src.private_impl.buf->data.ptr + a_src.private_impl.buf->meta.ri;
-    if (!a_src.private_impl.mark) {
-      a_src.private_impl.mark = iop_a_src;
-      a_src.private_impl.limit =
-          a_src.private_impl.buf->data.ptr + a_src.private_impl.buf->meta.wi;
-    }
-    io0_a_src = a_src.private_impl.mark;
-    io1_a_src = a_src.private_impl.limit;
+  uint8_t* io2_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
+  if (a_src) {
+    io0_a_src = a_src->data.ptr;
+    io1_a_src = io0_a_src + a_src->meta.ri;
+    iop_a_src = io1_a_src;
+    io2_a_src = io0_a_src + a_src->meta.wi;
   }
 
   uint32_t coro_susp_point = self->private_impl.p_decode_io_writer[0];
@@ -10851,7 +11344,7 @@
 
     {
       WUFFS_BASE__COROUTINE_SUSPENSION_POINT(1);
-      if (WUFFS_BASE__UNLIKELY(iop_a_src == io1_a_src)) {
+      if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
         status = wuffs_base__suspension__short_read;
         goto suspend;
       }
@@ -10864,7 +11357,7 @@
     }
     {
       WUFFS_BASE__COROUTINE_SUSPENSION_POINT(2);
-      if (WUFFS_BASE__UNLIKELY(iop_a_src == io1_a_src)) {
+      if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
         status = wuffs_base__suspension__short_read;
         goto suspend;
       }
@@ -10877,7 +11370,7 @@
     }
     {
       WUFFS_BASE__COROUTINE_SUSPENSION_POINT(3);
-      if (WUFFS_BASE__UNLIKELY(iop_a_src == io1_a_src)) {
+      if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
         status = wuffs_base__suspension__short_read;
         goto suspend;
       }
@@ -10890,7 +11383,7 @@
     }
     {
       WUFFS_BASE__COROUTINE_SUSPENSION_POINT(4);
-      if (WUFFS_BASE__UNLIKELY(iop_a_src == io1_a_src)) {
+      if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
         status = wuffs_base__suspension__short_read;
         goto suspend;
       }
@@ -10900,10 +11393,10 @@
     self->private_data.s_decode_io_writer[0].scratch = 6;
     WUFFS_BASE__COROUTINE_SUSPENSION_POINT(5);
     if (self->private_data.s_decode_io_writer[0].scratch >
-        ((uint64_t)(io1_a_src - iop_a_src))) {
+        ((uint64_t)(io2_a_src - iop_a_src))) {
       self->private_data.s_decode_io_writer[0].scratch -=
-          ((uint64_t)(io1_a_src - iop_a_src));
-      iop_a_src = io1_a_src;
+          ((uint64_t)(io2_a_src - iop_a_src));
+      iop_a_src = io2_a_src;
       status = wuffs_base__suspension__short_read;
       goto suspend;
     }
@@ -10912,14 +11405,14 @@
       {
         WUFFS_BASE__COROUTINE_SUSPENSION_POINT(6);
         uint16_t t_4;
-        if (WUFFS_BASE__LIKELY(io1_a_src - iop_a_src >= 2)) {
+        if (WUFFS_BASE__LIKELY(io2_a_src - iop_a_src >= 2)) {
           t_4 = wuffs_base__load_u16le(iop_a_src);
           iop_a_src += 2;
         } else {
           self->private_data.s_decode_io_writer[0].scratch = 0;
           WUFFS_BASE__COROUTINE_SUSPENSION_POINT(7);
           while (true) {
-            if (WUFFS_BASE__UNLIKELY(iop_a_src == io1_a_src)) {
+            if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
               status = wuffs_base__suspension__short_read;
               goto suspend;
             }
@@ -10942,10 +11435,10 @@
       self->private_data.s_decode_io_writer[0].scratch = ((uint32_t)(v_xlen));
       WUFFS_BASE__COROUTINE_SUSPENSION_POINT(8);
       if (self->private_data.s_decode_io_writer[0].scratch >
-          ((uint64_t)(io1_a_src - iop_a_src))) {
+          ((uint64_t)(io2_a_src - iop_a_src))) {
         self->private_data.s_decode_io_writer[0].scratch -=
-            ((uint64_t)(io1_a_src - iop_a_src));
-        iop_a_src = io1_a_src;
+            ((uint64_t)(io2_a_src - iop_a_src));
+        iop_a_src = io2_a_src;
         status = wuffs_base__suspension__short_read;
         goto suspend;
       }
@@ -10955,7 +11448,7 @@
       while (true) {
         {
           WUFFS_BASE__COROUTINE_SUSPENSION_POINT(9);
-          if (WUFFS_BASE__UNLIKELY(iop_a_src == io1_a_src)) {
+          if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
             status = wuffs_base__suspension__short_read;
             goto suspend;
           }
@@ -10972,7 +11465,7 @@
       while (true) {
         {
           WUFFS_BASE__COROUTINE_SUSPENSION_POINT(10);
-          if (WUFFS_BASE__UNLIKELY(iop_a_src == io1_a_src)) {
+          if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
             status = wuffs_base__suspension__short_read;
             goto suspend;
           }
@@ -10989,10 +11482,10 @@
       self->private_data.s_decode_io_writer[0].scratch = 2;
       WUFFS_BASE__COROUTINE_SUSPENSION_POINT(11);
       if (self->private_data.s_decode_io_writer[0].scratch >
-          ((uint64_t)(io1_a_src - iop_a_src))) {
+          ((uint64_t)(io2_a_src - iop_a_src))) {
         self->private_data.s_decode_io_writer[0].scratch -=
-            ((uint64_t)(io1_a_src - iop_a_src));
-        iop_a_src = io1_a_src;
+            ((uint64_t)(io2_a_src - iop_a_src));
+        iop_a_src = io2_a_src;
         status = wuffs_base__suspension__short_read;
         goto suspend;
       }
@@ -11003,36 +11496,33 @@
       goto exit;
     }
     while (true) {
-      wuffs_base__io_writer__set_mark(&a_dst, iop_a_dst);
+      v_mark = ((uint64_t)(iop_a_dst - io0_a_dst));
       {
-        if (a_dst.private_impl.buf) {
-          a_dst.private_impl.buf->meta.wi =
-              ((size_t)(iop_a_dst - a_dst.private_impl.buf->data.ptr));
+        if (a_dst) {
+          a_dst->meta.wi = ((size_t)(iop_a_dst - a_dst->data.ptr));
         }
-        if (a_src.private_impl.buf) {
-          a_src.private_impl.buf->meta.ri =
-              ((size_t)(iop_a_src - a_src.private_impl.buf->data.ptr));
+        if (a_src) {
+          a_src->meta.ri = ((size_t)(iop_a_src - a_src->data.ptr));
         }
         wuffs_base__status t_7 = wuffs_deflate__decoder__decode_io_writer(
             &self->private_data.f_flate, a_dst, a_src, a_workbuf);
-        if (a_dst.private_impl.buf) {
-          iop_a_dst = a_dst.private_impl.buf->data.ptr +
-                      a_dst.private_impl.buf->meta.wi;
+        if (a_dst) {
+          iop_a_dst = a_dst->data.ptr + a_dst->meta.wi;
         }
-        if (a_src.private_impl.buf) {
-          iop_a_src = a_src.private_impl.buf->data.ptr +
-                      a_src.private_impl.buf->meta.ri;
+        if (a_src) {
+          iop_a_src = a_src->data.ptr + a_src->meta.ri;
         }
         v_status = t_7;
       }
       if (!self->private_impl.f_ignore_checksum) {
         v_checksum_got = wuffs_crc32__ieee_hasher__update(
             &self->private_data.f_checksum,
-            wuffs_base__make_slice_u8(
-                a_dst.private_impl.mark,
-                (size_t)(iop_a_dst - a_dst.private_impl.mark)));
-        v_decoded_length_got += ((uint32_t)(
-            (((uint64_t)(iop_a_dst - a_dst.private_impl.mark)) & 4294967295)));
+            wuffs_base__io__since(v_mark, ((uint64_t)(iop_a_dst - io0_a_dst)),
+                                  io0_a_dst));
+        v_decoded_length_got +=
+            ((uint32_t)((wuffs_base__io__count_since(
+                             v_mark, ((uint64_t)(iop_a_dst - io0_a_dst))) &
+                         4294967295)));
       }
       if (wuffs_base__status__is_ok(v_status)) {
         goto label_2_break;
@@ -11044,14 +11534,14 @@
     {
       WUFFS_BASE__COROUTINE_SUSPENSION_POINT(13);
       uint32_t t_8;
-      if (WUFFS_BASE__LIKELY(io1_a_src - iop_a_src >= 4)) {
+      if (WUFFS_BASE__LIKELY(io2_a_src - iop_a_src >= 4)) {
         t_8 = wuffs_base__load_u32le(iop_a_src);
         iop_a_src += 4;
       } else {
         self->private_data.s_decode_io_writer[0].scratch = 0;
         WUFFS_BASE__COROUTINE_SUSPENSION_POINT(14);
         while (true) {
-          if (WUFFS_BASE__UNLIKELY(iop_a_src == io1_a_src)) {
+          if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
             status = wuffs_base__suspension__short_read;
             goto suspend;
           }
@@ -11073,14 +11563,14 @@
     {
       WUFFS_BASE__COROUTINE_SUSPENSION_POINT(15);
       uint32_t t_9;
-      if (WUFFS_BASE__LIKELY(io1_a_src - iop_a_src >= 4)) {
+      if (WUFFS_BASE__LIKELY(io2_a_src - iop_a_src >= 4)) {
         t_9 = wuffs_base__load_u32le(iop_a_src);
         iop_a_src += 4;
       } else {
         self->private_data.s_decode_io_writer[0].scratch = 0;
         WUFFS_BASE__COROUTINE_SUSPENSION_POINT(16);
         while (true) {
-          if (WUFFS_BASE__UNLIKELY(iop_a_src == io1_a_src)) {
+          if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
             status = wuffs_base__suspension__short_read;
             goto suspend;
           }
@@ -11114,8 +11604,10 @@
 
   goto suspend;
 suspend:
-  self->private_impl.p_decode_io_writer[0] = coro_susp_point;
-  self->private_impl.active_coroutine = 1;
+  self->private_impl.p_decode_io_writer[0] =
+      wuffs_base__status__is_suspension(status) ? coro_susp_point : 0;
+  self->private_impl.active_coroutine =
+      wuffs_base__status__is_suspension(status) ? 1 : 0;
   self->private_data.s_decode_io_writer[0].v_flags = v_flags;
   self->private_data.s_decode_io_writer[0].v_checksum_got = v_checksum_got;
   self->private_data.s_decode_io_writer[0].v_decoded_length_got =
@@ -11124,13 +11616,11 @@
 
   goto exit;
 exit:
-  if (a_dst.private_impl.buf) {
-    a_dst.private_impl.buf->meta.wi =
-        ((size_t)(iop_a_dst - a_dst.private_impl.buf->data.ptr));
+  if (a_dst) {
+    a_dst->meta.wi = ((size_t)(iop_a_dst - a_dst->data.ptr));
   }
-  if (a_src.private_impl.buf) {
-    a_src.private_impl.buf->meta.ri =
-        ((size_t)(iop_a_src - a_src.private_impl.buf->data.ptr));
+  if (a_src) {
+    a_src->meta.ri = ((size_t)(iop_a_src - a_src->data.ptr));
   }
 
   if (wuffs_base__status__is_error(status)) {
@@ -11265,8 +11755,8 @@
 
 WUFFS_BASE__MAYBE_STATIC wuffs_base__status  //
 wuffs_zlib__decoder__decode_io_writer(wuffs_zlib__decoder* self,
-                                      wuffs_base__io_writer a_dst,
-                                      wuffs_base__io_reader a_src,
+                                      wuffs_base__io_buffer* a_dst,
+                                      wuffs_base__io_buffer* a_src,
                                       wuffs_base__slice_u8 a_workbuf) {
   if (!self) {
     return wuffs_base__error__bad_receiver;
@@ -11276,6 +11766,10 @@
                ? wuffs_base__error__disabled_by_previous_error
                : wuffs_base__error__initialize_not_called;
   }
+  if (!a_dst || !a_src) {
+    self->private_impl.magic = WUFFS_BASE__DISABLED;
+    return wuffs_base__error__bad_argument;
+  }
   if ((self->private_impl.active_coroutine != 0) &&
       (self->private_impl.active_coroutine != 1)) {
     self->private_impl.magic = WUFFS_BASE__DISABLED;
@@ -11288,37 +11782,30 @@
   uint32_t v_checksum_got = 0;
   wuffs_base__status v_status = NULL;
   uint32_t v_checksum_want = 0;
+  uint64_t v_mark = 0;
 
   uint8_t* iop_a_dst = NULL;
   uint8_t* io0_a_dst WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
   uint8_t* io1_a_dst WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
-  if (a_dst.private_impl.buf) {
-    iop_a_dst =
-        a_dst.private_impl.buf->data.ptr + a_dst.private_impl.buf->meta.wi;
-    if (!a_dst.private_impl.mark) {
-      a_dst.private_impl.mark = iop_a_dst;
-      a_dst.private_impl.limit =
-          a_dst.private_impl.buf->data.ptr + a_dst.private_impl.buf->data.len;
+  uint8_t* io2_a_dst WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
+  if (a_dst) {
+    io0_a_dst = a_dst->data.ptr;
+    io1_a_dst = io0_a_dst + a_dst->meta.wi;
+    iop_a_dst = io1_a_dst;
+    io2_a_dst = io0_a_dst + a_dst->data.len;
+    if (a_dst->meta.closed) {
+      io2_a_dst = iop_a_dst;
     }
-    if (a_dst.private_impl.buf->meta.closed) {
-      a_dst.private_impl.limit = iop_a_dst;
-    }
-    io0_a_dst = a_dst.private_impl.mark;
-    io1_a_dst = a_dst.private_impl.limit;
   }
   uint8_t* iop_a_src = NULL;
   uint8_t* io0_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
   uint8_t* io1_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
-  if (a_src.private_impl.buf) {
-    iop_a_src =
-        a_src.private_impl.buf->data.ptr + a_src.private_impl.buf->meta.ri;
-    if (!a_src.private_impl.mark) {
-      a_src.private_impl.mark = iop_a_src;
-      a_src.private_impl.limit =
-          a_src.private_impl.buf->data.ptr + a_src.private_impl.buf->meta.wi;
-    }
-    io0_a_src = a_src.private_impl.mark;
-    io1_a_src = a_src.private_impl.limit;
+  uint8_t* io2_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
+  if (a_src) {
+    io0_a_src = a_src->data.ptr;
+    io1_a_src = io0_a_src + a_src->meta.ri;
+    iop_a_src = io1_a_src;
+    io2_a_src = io0_a_src + a_src->meta.wi;
   }
 
   uint32_t coro_susp_point = self->private_impl.p_decode_io_writer[0];
@@ -11331,14 +11818,14 @@
     {
       WUFFS_BASE__COROUTINE_SUSPENSION_POINT(1);
       uint16_t t_0;
-      if (WUFFS_BASE__LIKELY(io1_a_src - iop_a_src >= 2)) {
+      if (WUFFS_BASE__LIKELY(io2_a_src - iop_a_src >= 2)) {
         t_0 = wuffs_base__load_u16be(iop_a_src);
         iop_a_src += 2;
       } else {
         self->private_data.s_decode_io_writer[0].scratch = 0;
         WUFFS_BASE__COROUTINE_SUSPENSION_POINT(2);
         while (true) {
-          if (WUFFS_BASE__UNLIKELY(iop_a_src == io1_a_src)) {
+          if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
             status = wuffs_base__suspension__short_read;
             goto suspend;
           }
@@ -11374,34 +11861,29 @@
       goto exit;
     }
     while (true) {
-      wuffs_base__io_writer__set_mark(&a_dst, iop_a_dst);
+      v_mark = ((uint64_t)(iop_a_dst - io0_a_dst));
       {
-        if (a_dst.private_impl.buf) {
-          a_dst.private_impl.buf->meta.wi =
-              ((size_t)(iop_a_dst - a_dst.private_impl.buf->data.ptr));
+        if (a_dst) {
+          a_dst->meta.wi = ((size_t)(iop_a_dst - a_dst->data.ptr));
         }
-        if (a_src.private_impl.buf) {
-          a_src.private_impl.buf->meta.ri =
-              ((size_t)(iop_a_src - a_src.private_impl.buf->data.ptr));
+        if (a_src) {
+          a_src->meta.ri = ((size_t)(iop_a_src - a_src->data.ptr));
         }
         wuffs_base__status t_1 = wuffs_deflate__decoder__decode_io_writer(
             &self->private_data.f_flate, a_dst, a_src, a_workbuf);
-        if (a_dst.private_impl.buf) {
-          iop_a_dst = a_dst.private_impl.buf->data.ptr +
-                      a_dst.private_impl.buf->meta.wi;
+        if (a_dst) {
+          iop_a_dst = a_dst->data.ptr + a_dst->meta.wi;
         }
-        if (a_src.private_impl.buf) {
-          iop_a_src = a_src.private_impl.buf->data.ptr +
-                      a_src.private_impl.buf->meta.ri;
+        if (a_src) {
+          iop_a_src = a_src->data.ptr + a_src->meta.ri;
         }
         v_status = t_1;
       }
       if (!self->private_impl.f_ignore_checksum) {
         v_checksum_got = wuffs_adler32__hasher__update(
             &self->private_data.f_checksum,
-            wuffs_base__make_slice_u8(
-                a_dst.private_impl.mark,
-                (size_t)(iop_a_dst - a_dst.private_impl.mark)));
+            wuffs_base__io__since(v_mark, ((uint64_t)(iop_a_dst - io0_a_dst)),
+                                  io0_a_dst));
       }
       if (wuffs_base__status__is_ok(v_status)) {
         goto label_0_break;
@@ -11413,14 +11895,14 @@
     {
       WUFFS_BASE__COROUTINE_SUSPENSION_POINT(4);
       uint32_t t_2;
-      if (WUFFS_BASE__LIKELY(io1_a_src - iop_a_src >= 4)) {
+      if (WUFFS_BASE__LIKELY(io2_a_src - iop_a_src >= 4)) {
         t_2 = wuffs_base__load_u32be(iop_a_src);
         iop_a_src += 4;
       } else {
         self->private_data.s_decode_io_writer[0].scratch = 0;
         WUFFS_BASE__COROUTINE_SUSPENSION_POINT(5);
         while (true) {
-          if (WUFFS_BASE__UNLIKELY(iop_a_src == io1_a_src)) {
+          if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
             status = wuffs_base__suspension__short_read;
             goto suspend;
           }
@@ -11453,19 +11935,19 @@
 
   goto suspend;
 suspend:
-  self->private_impl.p_decode_io_writer[0] = coro_susp_point;
-  self->private_impl.active_coroutine = 1;
+  self->private_impl.p_decode_io_writer[0] =
+      wuffs_base__status__is_suspension(status) ? coro_susp_point : 0;
+  self->private_impl.active_coroutine =
+      wuffs_base__status__is_suspension(status) ? 1 : 0;
   self->private_data.s_decode_io_writer[0].v_checksum_got = v_checksum_got;
 
   goto exit;
 exit:
-  if (a_dst.private_impl.buf) {
-    a_dst.private_impl.buf->meta.wi =
-        ((size_t)(iop_a_dst - a_dst.private_impl.buf->data.ptr));
+  if (a_dst) {
+    a_dst->meta.wi = ((size_t)(iop_a_dst - a_dst->data.ptr));
   }
-  if (a_src.private_impl.buf) {
-    a_src.private_impl.buf->meta.ri =
-        ((size_t)(iop_a_src - a_src.private_impl.buf->data.ptr));
+  if (a_src) {
+    a_src->meta.ri = ((size_t)(iop_a_src - a_src->data.ptr));
   }
 
   if (wuffs_base__status__is_error(status)) {
diff --git a/release/c/wuffs-v0.2.c b/release/c/wuffs-v0.2.c
index 8ea6abd..2a0215b 100644
--- a/release/c/wuffs-v0.2.c
+++ b/release/c/wuffs-v0.2.c
@@ -60,15 +60,15 @@
 // each major.minor branch, the commit count should increase monotonically.
 //
 // WUFFS_VERSION was overridden by "wuffs gen -version" based on revision
-// 6b9f658c593bb57851e3fac7df2e5f3949a4fac8 committed on 2019-04-07.
+// 3037f1e389b54f9721161cc92c9b00372254cafc committed on 2019-07-07.
 #define WUFFS_VERSION ((uint64_t)0x0000000000020000)
 #define WUFFS_VERSION_MAJOR ((uint64_t)0x00000000)
 #define WUFFS_VERSION_MINOR ((uint64_t)0x0002)
 #define WUFFS_VERSION_PATCH ((uint64_t)0x0000)
-#define WUFFS_VERSION_PRE_RELEASE_LABEL "alpha.36"
-#define WUFFS_VERSION_BUILD_METADATA_COMMIT_COUNT 1675
-#define WUFFS_VERSION_BUILD_METADATA_COMMIT_DATE 20190407
-#define WUFFS_VERSION_STRING "0.2.0-alpha.36+1675.20190407"
+#define WUFFS_VERSION_PRE_RELEASE_LABEL "alpha.44"
+#define WUFFS_VERSION_BUILD_METADATA_COMMIT_COUNT 1776
+#define WUFFS_VERSION_BUILD_METADATA_COMMIT_DATE 20190707
+#define WUFFS_VERSION_STRING "0.2.0-alpha.44+1776.20190707"
 
 // Define WUFFS_CONFIG__STATIC_FUNCTIONS to make all of Wuffs' functions have
 // static storage. The motivation is discussed in the "ALLOW STATIC
@@ -192,8 +192,10 @@
 typedef const char* wuffs_base__status;
 
 extern const char* wuffs_base__warning__end_of_data;
+extern const char* wuffs_base__warning__metadata_reported;
 extern const char* wuffs_base__suspension__short_read;
 extern const char* wuffs_base__suspension__short_write;
+extern const char* wuffs_base__error__bad_i_o_position;
 extern const char* wuffs_base__error__bad_argument_length_too_short;
 extern const char* wuffs_base__error__bad_argument;
 extern const char* wuffs_base__error__bad_call_sequence;
@@ -208,6 +210,7 @@
 extern const char* wuffs_base__error__initialize_not_called;
 extern const char* wuffs_base__error__interleaved_coroutine_calls;
 extern const char* wuffs_base__error__not_enough_data;
+extern const char* wuffs_base__error__unsupported_option;
 extern const char* wuffs_base__error__too_much_data;
 
 static inline bool  //
@@ -237,6 +240,16 @@
 
 // --------
 
+// FourCC constants.
+
+// International Color Consortium Profile.
+#define WUFFS_BASE__FOURCC__ICCP 0x49434350
+
+// Extensible Metadata Platform.
+#define WUFFS_BASE__FOURCC__XMP 0x584D5020
+
+// --------
+
 // Flicks are a unit of time. One flick (frame-tick) is 1 / 705_600_000 of a
 // second. See https://github.com/OculusVR/Flicks
 typedef int64_t wuffs_base__flicks;
@@ -510,14 +523,14 @@
   uint32_t max_incl;
 
 #ifdef __cplusplus
-  inline bool is_empty();
-  inline bool equals(wuffs_base__range_ii_u32__struct s);
+  inline bool is_empty() const;
+  inline bool equals(wuffs_base__range_ii_u32__struct s) const;
   inline wuffs_base__range_ii_u32__struct intersect(
-      wuffs_base__range_ii_u32__struct s);
+      wuffs_base__range_ii_u32__struct s) const;
   inline wuffs_base__range_ii_u32__struct unite(
-      wuffs_base__range_ii_u32__struct s);
-  inline bool contains(uint32_t x);
-  inline bool contains_range(wuffs_base__range_ii_u32__struct s);
+      wuffs_base__range_ii_u32__struct s) const;
+  inline bool contains(uint32_t x) const;
+  inline bool contains_range(wuffs_base__range_ii_u32__struct s) const;
 #endif  // __cplusplus
 
 } wuffs_base__range_ii_u32;
@@ -531,12 +544,12 @@
 }
 
 static inline bool  //
-wuffs_base__range_ii_u32__is_empty(wuffs_base__range_ii_u32* r) {
+wuffs_base__range_ii_u32__is_empty(const wuffs_base__range_ii_u32* r) {
   return r->min_incl > r->max_incl;
 }
 
 static inline bool  //
-wuffs_base__range_ii_u32__equals(wuffs_base__range_ii_u32* r,
+wuffs_base__range_ii_u32__equals(const wuffs_base__range_ii_u32* r,
                                  wuffs_base__range_ii_u32 s) {
   return (r->min_incl == s.min_incl && r->max_incl == s.max_incl) ||
          (wuffs_base__range_ii_u32__is_empty(r) &&
@@ -544,7 +557,7 @@
 }
 
 static inline wuffs_base__range_ii_u32  //
-wuffs_base__range_ii_u32__intersect(wuffs_base__range_ii_u32* r,
+wuffs_base__range_ii_u32__intersect(const wuffs_base__range_ii_u32* r,
                                     wuffs_base__range_ii_u32 s) {
   wuffs_base__range_ii_u32 t;
   t.min_incl = wuffs_base__u32__max(r->min_incl, s.min_incl);
@@ -553,7 +566,7 @@
 }
 
 static inline wuffs_base__range_ii_u32  //
-wuffs_base__range_ii_u32__unite(wuffs_base__range_ii_u32* r,
+wuffs_base__range_ii_u32__unite(const wuffs_base__range_ii_u32* r,
                                 wuffs_base__range_ii_u32 s) {
   if (wuffs_base__range_ii_u32__is_empty(r)) {
     return s;
@@ -568,12 +581,13 @@
 }
 
 static inline bool  //
-wuffs_base__range_ii_u32__contains(wuffs_base__range_ii_u32* r, uint32_t x) {
+wuffs_base__range_ii_u32__contains(const wuffs_base__range_ii_u32* r,
+                                   uint32_t x) {
   return (r->min_incl <= x) && (x <= r->max_incl);
 }
 
 static inline bool  //
-wuffs_base__range_ii_u32__contains_range(wuffs_base__range_ii_u32* r,
+wuffs_base__range_ii_u32__contains_range(const wuffs_base__range_ii_u32* r,
                                          wuffs_base__range_ii_u32 s) {
   return wuffs_base__range_ii_u32__equals(
       &s, wuffs_base__range_ii_u32__intersect(r, s));
@@ -582,32 +596,32 @@
 #ifdef __cplusplus
 
 inline bool  //
-wuffs_base__range_ii_u32::is_empty() {
+wuffs_base__range_ii_u32::is_empty() const {
   return wuffs_base__range_ii_u32__is_empty(this);
 }
 
 inline bool  //
-wuffs_base__range_ii_u32::equals(wuffs_base__range_ii_u32 s) {
+wuffs_base__range_ii_u32::equals(wuffs_base__range_ii_u32 s) const {
   return wuffs_base__range_ii_u32__equals(this, s);
 }
 
 inline wuffs_base__range_ii_u32  //
-wuffs_base__range_ii_u32::intersect(wuffs_base__range_ii_u32 s) {
+wuffs_base__range_ii_u32::intersect(wuffs_base__range_ii_u32 s) const {
   return wuffs_base__range_ii_u32__intersect(this, s);
 }
 
 inline wuffs_base__range_ii_u32  //
-wuffs_base__range_ii_u32::unite(wuffs_base__range_ii_u32 s) {
+wuffs_base__range_ii_u32::unite(wuffs_base__range_ii_u32 s) const {
   return wuffs_base__range_ii_u32__unite(this, s);
 }
 
 inline bool  //
-wuffs_base__range_ii_u32::contains(uint32_t x) {
+wuffs_base__range_ii_u32::contains(uint32_t x) const {
   return wuffs_base__range_ii_u32__contains(this, x);
 }
 
 inline bool  //
-wuffs_base__range_ii_u32::contains_range(wuffs_base__range_ii_u32 s) {
+wuffs_base__range_ii_u32::contains_range(wuffs_base__range_ii_u32 s) const {
   return wuffs_base__range_ii_u32__contains_range(this, s);
 }
 
@@ -620,15 +634,15 @@
   uint32_t max_excl;
 
 #ifdef __cplusplus
-  inline bool is_empty();
-  inline bool equals(wuffs_base__range_ie_u32__struct s);
+  inline bool is_empty() const;
+  inline bool equals(wuffs_base__range_ie_u32__struct s) const;
   inline wuffs_base__range_ie_u32__struct intersect(
-      wuffs_base__range_ie_u32__struct s);
+      wuffs_base__range_ie_u32__struct s) const;
   inline wuffs_base__range_ie_u32__struct unite(
-      wuffs_base__range_ie_u32__struct s);
-  inline bool contains(uint32_t x);
-  inline bool contains_range(wuffs_base__range_ie_u32__struct s);
-  inline uint32_t length();
+      wuffs_base__range_ie_u32__struct s) const;
+  inline bool contains(uint32_t x) const;
+  inline bool contains_range(wuffs_base__range_ie_u32__struct s) const;
+  inline uint32_t length() const;
 #endif  // __cplusplus
 
 } wuffs_base__range_ie_u32;
@@ -642,12 +656,12 @@
 }
 
 static inline bool  //
-wuffs_base__range_ie_u32__is_empty(wuffs_base__range_ie_u32* r) {
+wuffs_base__range_ie_u32__is_empty(const wuffs_base__range_ie_u32* r) {
   return r->min_incl >= r->max_excl;
 }
 
 static inline bool  //
-wuffs_base__range_ie_u32__equals(wuffs_base__range_ie_u32* r,
+wuffs_base__range_ie_u32__equals(const wuffs_base__range_ie_u32* r,
                                  wuffs_base__range_ie_u32 s) {
   return (r->min_incl == s.min_incl && r->max_excl == s.max_excl) ||
          (wuffs_base__range_ie_u32__is_empty(r) &&
@@ -655,7 +669,7 @@
 }
 
 static inline wuffs_base__range_ie_u32  //
-wuffs_base__range_ie_u32__intersect(wuffs_base__range_ie_u32* r,
+wuffs_base__range_ie_u32__intersect(const wuffs_base__range_ie_u32* r,
                                     wuffs_base__range_ie_u32 s) {
   wuffs_base__range_ie_u32 t;
   t.min_incl = wuffs_base__u32__max(r->min_incl, s.min_incl);
@@ -664,7 +678,7 @@
 }
 
 static inline wuffs_base__range_ie_u32  //
-wuffs_base__range_ie_u32__unite(wuffs_base__range_ie_u32* r,
+wuffs_base__range_ie_u32__unite(const wuffs_base__range_ie_u32* r,
                                 wuffs_base__range_ie_u32 s) {
   if (wuffs_base__range_ie_u32__is_empty(r)) {
     return s;
@@ -679,56 +693,57 @@
 }
 
 static inline bool  //
-wuffs_base__range_ie_u32__contains(wuffs_base__range_ie_u32* r, uint32_t x) {
+wuffs_base__range_ie_u32__contains(const wuffs_base__range_ie_u32* r,
+                                   uint32_t x) {
   return (r->min_incl <= x) && (x < r->max_excl);
 }
 
 static inline bool  //
-wuffs_base__range_ie_u32__contains_range(wuffs_base__range_ie_u32* r,
+wuffs_base__range_ie_u32__contains_range(const wuffs_base__range_ie_u32* r,
                                          wuffs_base__range_ie_u32 s) {
   return wuffs_base__range_ie_u32__equals(
       &s, wuffs_base__range_ie_u32__intersect(r, s));
 }
 
 static inline uint32_t  //
-wuffs_base__range_ie_u32__length(wuffs_base__range_ie_u32* r) {
+wuffs_base__range_ie_u32__length(const wuffs_base__range_ie_u32* r) {
   return wuffs_base__u32__sat_sub(r->max_excl, r->min_incl);
 }
 
 #ifdef __cplusplus
 
 inline bool  //
-wuffs_base__range_ie_u32::is_empty() {
+wuffs_base__range_ie_u32::is_empty() const {
   return wuffs_base__range_ie_u32__is_empty(this);
 }
 
 inline bool  //
-wuffs_base__range_ie_u32::equals(wuffs_base__range_ie_u32 s) {
+wuffs_base__range_ie_u32::equals(wuffs_base__range_ie_u32 s) const {
   return wuffs_base__range_ie_u32__equals(this, s);
 }
 
 inline wuffs_base__range_ie_u32  //
-wuffs_base__range_ie_u32::intersect(wuffs_base__range_ie_u32 s) {
+wuffs_base__range_ie_u32::intersect(wuffs_base__range_ie_u32 s) const {
   return wuffs_base__range_ie_u32__intersect(this, s);
 }
 
 inline wuffs_base__range_ie_u32  //
-wuffs_base__range_ie_u32::unite(wuffs_base__range_ie_u32 s) {
+wuffs_base__range_ie_u32::unite(wuffs_base__range_ie_u32 s) const {
   return wuffs_base__range_ie_u32__unite(this, s);
 }
 
 inline bool  //
-wuffs_base__range_ie_u32::contains(uint32_t x) {
+wuffs_base__range_ie_u32::contains(uint32_t x) const {
   return wuffs_base__range_ie_u32__contains(this, x);
 }
 
 inline bool  //
-wuffs_base__range_ie_u32::contains_range(wuffs_base__range_ie_u32 s) {
+wuffs_base__range_ie_u32::contains_range(wuffs_base__range_ie_u32 s) const {
   return wuffs_base__range_ie_u32__contains_range(this, s);
 }
 
 inline uint32_t  //
-wuffs_base__range_ie_u32::length() {
+wuffs_base__range_ie_u32::length() const {
   return wuffs_base__range_ie_u32__length(this);
 }
 
@@ -741,14 +756,14 @@
   uint64_t max_incl;
 
 #ifdef __cplusplus
-  inline bool is_empty();
-  inline bool equals(wuffs_base__range_ii_u64__struct s);
+  inline bool is_empty() const;
+  inline bool equals(wuffs_base__range_ii_u64__struct s) const;
   inline wuffs_base__range_ii_u64__struct intersect(
-      wuffs_base__range_ii_u64__struct s);
+      wuffs_base__range_ii_u64__struct s) const;
   inline wuffs_base__range_ii_u64__struct unite(
-      wuffs_base__range_ii_u64__struct s);
-  inline bool contains(uint64_t x);
-  inline bool contains_range(wuffs_base__range_ii_u64__struct s);
+      wuffs_base__range_ii_u64__struct s) const;
+  inline bool contains(uint64_t x) const;
+  inline bool contains_range(wuffs_base__range_ii_u64__struct s) const;
 #endif  // __cplusplus
 
 } wuffs_base__range_ii_u64;
@@ -762,12 +777,12 @@
 }
 
 static inline bool  //
-wuffs_base__range_ii_u64__is_empty(wuffs_base__range_ii_u64* r) {
+wuffs_base__range_ii_u64__is_empty(const wuffs_base__range_ii_u64* r) {
   return r->min_incl > r->max_incl;
 }
 
 static inline bool  //
-wuffs_base__range_ii_u64__equals(wuffs_base__range_ii_u64* r,
+wuffs_base__range_ii_u64__equals(const wuffs_base__range_ii_u64* r,
                                  wuffs_base__range_ii_u64 s) {
   return (r->min_incl == s.min_incl && r->max_incl == s.max_incl) ||
          (wuffs_base__range_ii_u64__is_empty(r) &&
@@ -775,7 +790,7 @@
 }
 
 static inline wuffs_base__range_ii_u64  //
-wuffs_base__range_ii_u64__intersect(wuffs_base__range_ii_u64* r,
+wuffs_base__range_ii_u64__intersect(const wuffs_base__range_ii_u64* r,
                                     wuffs_base__range_ii_u64 s) {
   wuffs_base__range_ii_u64 t;
   t.min_incl = wuffs_base__u64__max(r->min_incl, s.min_incl);
@@ -784,7 +799,7 @@
 }
 
 static inline wuffs_base__range_ii_u64  //
-wuffs_base__range_ii_u64__unite(wuffs_base__range_ii_u64* r,
+wuffs_base__range_ii_u64__unite(const wuffs_base__range_ii_u64* r,
                                 wuffs_base__range_ii_u64 s) {
   if (wuffs_base__range_ii_u64__is_empty(r)) {
     return s;
@@ -799,12 +814,13 @@
 }
 
 static inline bool  //
-wuffs_base__range_ii_u64__contains(wuffs_base__range_ii_u64* r, uint64_t x) {
+wuffs_base__range_ii_u64__contains(const wuffs_base__range_ii_u64* r,
+                                   uint64_t x) {
   return (r->min_incl <= x) && (x <= r->max_incl);
 }
 
 static inline bool  //
-wuffs_base__range_ii_u64__contains_range(wuffs_base__range_ii_u64* r,
+wuffs_base__range_ii_u64__contains_range(const wuffs_base__range_ii_u64* r,
                                          wuffs_base__range_ii_u64 s) {
   return wuffs_base__range_ii_u64__equals(
       &s, wuffs_base__range_ii_u64__intersect(r, s));
@@ -813,32 +829,32 @@
 #ifdef __cplusplus
 
 inline bool  //
-wuffs_base__range_ii_u64::is_empty() {
+wuffs_base__range_ii_u64::is_empty() const {
   return wuffs_base__range_ii_u64__is_empty(this);
 }
 
 inline bool  //
-wuffs_base__range_ii_u64::equals(wuffs_base__range_ii_u64 s) {
+wuffs_base__range_ii_u64::equals(wuffs_base__range_ii_u64 s) const {
   return wuffs_base__range_ii_u64__equals(this, s);
 }
 
 inline wuffs_base__range_ii_u64  //
-wuffs_base__range_ii_u64::intersect(wuffs_base__range_ii_u64 s) {
+wuffs_base__range_ii_u64::intersect(wuffs_base__range_ii_u64 s) const {
   return wuffs_base__range_ii_u64__intersect(this, s);
 }
 
 inline wuffs_base__range_ii_u64  //
-wuffs_base__range_ii_u64::unite(wuffs_base__range_ii_u64 s) {
+wuffs_base__range_ii_u64::unite(wuffs_base__range_ii_u64 s) const {
   return wuffs_base__range_ii_u64__unite(this, s);
 }
 
 inline bool  //
-wuffs_base__range_ii_u64::contains(uint64_t x) {
+wuffs_base__range_ii_u64::contains(uint64_t x) const {
   return wuffs_base__range_ii_u64__contains(this, x);
 }
 
 inline bool  //
-wuffs_base__range_ii_u64::contains_range(wuffs_base__range_ii_u64 s) {
+wuffs_base__range_ii_u64::contains_range(wuffs_base__range_ii_u64 s) const {
   return wuffs_base__range_ii_u64__contains_range(this, s);
 }
 
@@ -851,15 +867,15 @@
   uint64_t max_excl;
 
 #ifdef __cplusplus
-  inline bool is_empty();
-  inline bool equals(wuffs_base__range_ie_u64__struct s);
+  inline bool is_empty() const;
+  inline bool equals(wuffs_base__range_ie_u64__struct s) const;
   inline wuffs_base__range_ie_u64__struct intersect(
-      wuffs_base__range_ie_u64__struct s);
+      wuffs_base__range_ie_u64__struct s) const;
   inline wuffs_base__range_ie_u64__struct unite(
-      wuffs_base__range_ie_u64__struct s);
-  inline bool contains(uint64_t x);
-  inline bool contains_range(wuffs_base__range_ie_u64__struct s);
-  inline uint64_t length();
+      wuffs_base__range_ie_u64__struct s) const;
+  inline bool contains(uint64_t x) const;
+  inline bool contains_range(wuffs_base__range_ie_u64__struct s) const;
+  inline uint64_t length() const;
 #endif  // __cplusplus
 
 } wuffs_base__range_ie_u64;
@@ -873,12 +889,12 @@
 }
 
 static inline bool  //
-wuffs_base__range_ie_u64__is_empty(wuffs_base__range_ie_u64* r) {
+wuffs_base__range_ie_u64__is_empty(const wuffs_base__range_ie_u64* r) {
   return r->min_incl >= r->max_excl;
 }
 
 static inline bool  //
-wuffs_base__range_ie_u64__equals(wuffs_base__range_ie_u64* r,
+wuffs_base__range_ie_u64__equals(const wuffs_base__range_ie_u64* r,
                                  wuffs_base__range_ie_u64 s) {
   return (r->min_incl == s.min_incl && r->max_excl == s.max_excl) ||
          (wuffs_base__range_ie_u64__is_empty(r) &&
@@ -886,7 +902,7 @@
 }
 
 static inline wuffs_base__range_ie_u64  //
-wuffs_base__range_ie_u64__intersect(wuffs_base__range_ie_u64* r,
+wuffs_base__range_ie_u64__intersect(const wuffs_base__range_ie_u64* r,
                                     wuffs_base__range_ie_u64 s) {
   wuffs_base__range_ie_u64 t;
   t.min_incl = wuffs_base__u64__max(r->min_incl, s.min_incl);
@@ -895,7 +911,7 @@
 }
 
 static inline wuffs_base__range_ie_u64  //
-wuffs_base__range_ie_u64__unite(wuffs_base__range_ie_u64* r,
+wuffs_base__range_ie_u64__unite(const wuffs_base__range_ie_u64* r,
                                 wuffs_base__range_ie_u64 s) {
   if (wuffs_base__range_ie_u64__is_empty(r)) {
     return s;
@@ -910,56 +926,57 @@
 }
 
 static inline bool  //
-wuffs_base__range_ie_u64__contains(wuffs_base__range_ie_u64* r, uint64_t x) {
+wuffs_base__range_ie_u64__contains(const wuffs_base__range_ie_u64* r,
+                                   uint64_t x) {
   return (r->min_incl <= x) && (x < r->max_excl);
 }
 
 static inline bool  //
-wuffs_base__range_ie_u64__contains_range(wuffs_base__range_ie_u64* r,
+wuffs_base__range_ie_u64__contains_range(const wuffs_base__range_ie_u64* r,
                                          wuffs_base__range_ie_u64 s) {
   return wuffs_base__range_ie_u64__equals(
       &s, wuffs_base__range_ie_u64__intersect(r, s));
 }
 
 static inline uint64_t  //
-wuffs_base__range_ie_u64__length(wuffs_base__range_ie_u64* r) {
+wuffs_base__range_ie_u64__length(const wuffs_base__range_ie_u64* r) {
   return wuffs_base__u64__sat_sub(r->max_excl, r->min_incl);
 }
 
 #ifdef __cplusplus
 
 inline bool  //
-wuffs_base__range_ie_u64::is_empty() {
+wuffs_base__range_ie_u64::is_empty() const {
   return wuffs_base__range_ie_u64__is_empty(this);
 }
 
 inline bool  //
-wuffs_base__range_ie_u64::equals(wuffs_base__range_ie_u64 s) {
+wuffs_base__range_ie_u64::equals(wuffs_base__range_ie_u64 s) const {
   return wuffs_base__range_ie_u64__equals(this, s);
 }
 
 inline wuffs_base__range_ie_u64  //
-wuffs_base__range_ie_u64::intersect(wuffs_base__range_ie_u64 s) {
+wuffs_base__range_ie_u64::intersect(wuffs_base__range_ie_u64 s) const {
   return wuffs_base__range_ie_u64__intersect(this, s);
 }
 
 inline wuffs_base__range_ie_u64  //
-wuffs_base__range_ie_u64::unite(wuffs_base__range_ie_u64 s) {
+wuffs_base__range_ie_u64::unite(wuffs_base__range_ie_u64 s) const {
   return wuffs_base__range_ie_u64__unite(this, s);
 }
 
 inline bool  //
-wuffs_base__range_ie_u64::contains(uint64_t x) {
+wuffs_base__range_ie_u64::contains(uint64_t x) const {
   return wuffs_base__range_ie_u64__contains(this, x);
 }
 
 inline bool  //
-wuffs_base__range_ie_u64::contains_range(wuffs_base__range_ie_u64 s) {
+wuffs_base__range_ie_u64::contains_range(wuffs_base__range_ie_u64 s) const {
   return wuffs_base__range_ie_u64__contains_range(this, s);
 }
 
 inline uint64_t  //
-wuffs_base__range_ie_u64::length() {
+wuffs_base__range_ie_u64::length() const {
   return wuffs_base__range_ie_u64__length(this);
 }
 
@@ -983,14 +1000,14 @@
   uint32_t max_incl_y;
 
 #ifdef __cplusplus
-  inline bool is_empty();
-  inline bool equals(wuffs_base__rect_ii_u32__struct s);
+  inline bool is_empty() const;
+  inline bool equals(wuffs_base__rect_ii_u32__struct s) const;
   inline wuffs_base__rect_ii_u32__struct intersect(
-      wuffs_base__rect_ii_u32__struct s);
+      wuffs_base__rect_ii_u32__struct s) const;
   inline wuffs_base__rect_ii_u32__struct unite(
-      wuffs_base__rect_ii_u32__struct s);
-  inline bool contains(uint32_t x, uint32_t y);
-  inline bool contains_rect(wuffs_base__rect_ii_u32__struct s);
+      wuffs_base__rect_ii_u32__struct s) const;
+  inline bool contains(uint32_t x, uint32_t y) const;
+  inline bool contains_rect(wuffs_base__rect_ii_u32__struct s) const;
 #endif  // __cplusplus
 
 } wuffs_base__rect_ii_u32;<