Factor out fuzz/c/fuzzlib/fuzzlib_image_decoder.c
diff --git a/fuzz/c/fuzzlib/fuzzlib_image_decoder.c b/fuzz/c/fuzzlib/fuzzlib_image_decoder.c
new file mode 100644
index 0000000..ae3d979
--- /dev/null
+++ b/fuzz/c/fuzzlib/fuzzlib_image_decoder.c
@@ -0,0 +1,129 @@
+// Copyright 2020 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.
+
+#ifndef WUFFS_INCLUDE_GUARD
+#error "Wuffs' .h files need to be included before this file"
+#endif
+
+static const char*  //
+fuzz_image_decoder(wuffs_base__io_buffer* src,
+                   uint64_t hash,
+                   wuffs_base__image_decoder* dec) {
+  const char* ret = NULL;
+  wuffs_base__slice_u8 pixbuf = ((wuffs_base__slice_u8){});
+  wuffs_base__slice_u8 workbuf = ((wuffs_base__slice_u8){});
+
+  // Use a {} code block so that "goto exit" doesn't trigger "jump bypasses
+  // variable initialization" warnings.
+  {
+    wuffs_base__image_config ic = ((wuffs_base__image_config){});
+    wuffs_base__status status =
+        wuffs_base__image_decoder__decode_image_config(dec, &ic, src);
+    if (!wuffs_base__status__is_ok(&status)) {
+      ret = wuffs_base__status__message(&status);
+      goto exit;
+    }
+    if (!wuffs_base__image_config__is_valid(&ic)) {
+      ret = "invalid image_config";
+      goto exit;
+    }
+
+    // 50% of the time, choose BGRA_PREMUL instead of the native pixel config.
+    if (hash & 1) {
+      wuffs_base__pixel_config__set(
+          &ic.pixcfg, WUFFS_BASE__PIXEL_FORMAT__BGRA_PREMUL,
+          WUFFS_BASE__PIXEL_SUBSAMPLING__NONE,
+          wuffs_base__pixel_config__width(&ic.pixcfg),
+          wuffs_base__pixel_config__height(&ic.pixcfg));
+    }
+    hash >>= 1;
+
+    // Wuffs allows either statically or dynamically allocated work buffers.
+    // This program exercises dynamic allocation.
+    uint64_t n = wuffs_base__image_decoder__workbuf_len(dec).max_incl;
+    if (n > 64 * 1024 * 1024) {  // Don't allocate more than 64 MiB.
+      ret = "image too large";
+      goto exit;
+    }
+    if (n > 0) {
+      workbuf = wuffs_base__malloc_slice_u8(malloc, n);
+      if (!workbuf.ptr) {
+        ret = "out of memory";
+        goto exit;
+      }
+    }
+
+    n = wuffs_base__pixel_config__pixbuf_len(&ic.pixcfg);
+    if (n > 64 * 1024 * 1024) {  // Don't allocate more than 64 MiB.
+      ret = "image too large";
+      goto exit;
+    }
+    if (n > 0) {
+      pixbuf = wuffs_base__malloc_slice_u8(malloc, n);
+      if (!pixbuf.ptr) {
+        ret = "out of memory";
+        goto exit;
+      }
+    }
+
+    wuffs_base__pixel_buffer pb = ((wuffs_base__pixel_buffer){});
+    status = wuffs_base__pixel_buffer__set_from_slice(&pb, &ic.pixcfg, pixbuf);
+    if (!wuffs_base__status__is_ok(&status)) {
+      ret = wuffs_base__status__message(&status);
+      goto exit;
+    }
+
+    bool seen_ok = false;
+    while (true) {
+      wuffs_base__frame_config fc = ((wuffs_base__frame_config){});
+      status = wuffs_base__image_decoder__decode_frame_config(dec, &fc, src);
+      if (!wuffs_base__status__is_ok(&status)) {
+        if ((status.repr != wuffs_base__note__end_of_data) || !seen_ok) {
+          ret = wuffs_base__status__message(&status);
+        }
+        goto exit;
+      }
+
+      status = wuffs_base__image_decoder__decode_frame(
+          dec, &pb, src, WUFFS_BASE__PIXEL_BLEND__SRC, workbuf, NULL);
+
+      wuffs_base__rect_ie_u32 frame_rect =
+          wuffs_base__frame_config__bounds(&fc);
+      wuffs_base__rect_ie_u32 dirty_rect =
+          wuffs_base__image_decoder__frame_dirty_rect(dec);
+      if (!wuffs_base__rect_ie_u32__contains_rect(&frame_rect, dirty_rect)) {
+        ret = "internal error: frame_rect does not contain dirty_rect";
+        goto exit;
+      }
+
+      if (!wuffs_base__status__is_ok(&status)) {
+        if ((status.repr != wuffs_base__note__end_of_data) || !seen_ok) {
+          ret = wuffs_base__status__message(&status);
+        }
+        goto exit;
+      }
+      seen_ok = true;
+
+      if (!wuffs_base__rect_ie_u32__equals(&frame_rect, dirty_rect)) {
+        ret = "internal error: frame_rect does not equal dirty_rect";
+        goto exit;
+      }
+    }
+  }
+
+exit:
+  free(workbuf.ptr);
+  free(pixbuf.ptr);
+  return ret;
+}
diff --git a/fuzz/c/std/gif_fuzzer.c b/fuzz/c/std/gif_fuzzer.c
index e8950a4..0f5de60 100644
--- a/fuzz/c/std/gif_fuzzer.c
+++ b/fuzz/c/std/gif_fuzzer.c
@@ -59,123 +59,19 @@
 // program to generate a stand-alone C file.
 #include "../../../release/c/wuffs-unsupported-snapshot.c"
 #include "../fuzzlib/fuzzlib.c"
+#include "../fuzzlib/fuzzlib_image_decoder.c"
 
 const char*  //
 fuzz(wuffs_base__io_buffer* src, uint64_t hash) {
-  const char* ret = NULL;
-  wuffs_base__slice_u8 pixbuf = ((wuffs_base__slice_u8){});
-  wuffs_base__slice_u8 workbuf = ((wuffs_base__slice_u8){});
-
-  // Use a {} code block so that "goto exit" doesn't trigger "jump bypasses
-  // variable initialization" warnings.
-  {
-    wuffs_gif__decoder dec;
-    wuffs_base__status status = wuffs_gif__decoder__initialize(
-        &dec, sizeof dec, WUFFS_VERSION,
-        (hash & 1) ? WUFFS_INITIALIZE__LEAVE_INTERNAL_BUFFERS_UNINITIALIZED
-                   : 0);
-    hash >>= 1;
-    if (!wuffs_base__status__is_ok(&status)) {
-      ret = wuffs_base__status__message(&status);
-      goto exit;
-    }
-
-    wuffs_base__image_config ic = ((wuffs_base__image_config){});
-    status = wuffs_gif__decoder__decode_image_config(&dec, &ic, src);
-    if (!wuffs_base__status__is_ok(&status)) {
-      ret = wuffs_base__status__message(&status);
-      goto exit;
-    }
-    if (!wuffs_base__image_config__is_valid(&ic)) {
-      ret = "invalid image_config";
-      goto exit;
-    }
-
-    // 50% of the time, choose BGRA_PREMUL instead of the native pixel config.
-    if (hash & 1) {
-      wuffs_base__pixel_config__set(
-          &ic.pixcfg, WUFFS_BASE__PIXEL_FORMAT__BGRA_PREMUL,
-          WUFFS_BASE__PIXEL_SUBSAMPLING__NONE,
-          wuffs_base__pixel_config__width(&ic.pixcfg),
-          wuffs_base__pixel_config__height(&ic.pixcfg));
-    }
-    hash >>= 1;
-
-    // Wuffs allows either statically or dynamically allocated work buffers.
-    // This program exercises dynamic allocation.
-    uint64_t n = wuffs_gif__decoder__workbuf_len(&dec).max_incl;
-    if (n > 64 * 1024 * 1024) {  // Don't allocate more than 64 MiB.
-      ret = "image too large";
-      goto exit;
-    }
-    if (n > 0) {
-      workbuf = wuffs_base__malloc_slice_u8(malloc, n);
-      if (!workbuf.ptr) {
-        ret = "out of memory";
-        goto exit;
-      }
-    }
-
-    n = wuffs_base__pixel_config__pixbuf_len(&ic.pixcfg);
-    if (n > 64 * 1024 * 1024) {  // Don't allocate more than 64 MiB.
-      ret = "image too large";
-      goto exit;
-    }
-    if (n > 0) {
-      pixbuf = wuffs_base__malloc_slice_u8(malloc, n);
-      if (!pixbuf.ptr) {
-        ret = "out of memory";
-        goto exit;
-      }
-    }
-
-    wuffs_base__pixel_buffer pb = ((wuffs_base__pixel_buffer){});
-    status = wuffs_base__pixel_buffer__set_from_slice(&pb, &ic.pixcfg, pixbuf);
-    if (!wuffs_base__status__is_ok(&status)) {
-      ret = wuffs_base__status__message(&status);
-      goto exit;
-    }
-
-    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);
-      if (!wuffs_base__status__is_ok(&status)) {
-        if ((status.repr != wuffs_base__note__end_of_data) || !seen_ok) {
-          ret = wuffs_base__status__message(&status);
-        }
-        goto exit;
-      }
-
-      status = wuffs_gif__decoder__decode_frame(
-          &dec, &pb, src, WUFFS_BASE__PIXEL_BLEND__SRC, workbuf, NULL);
-
-      wuffs_base__rect_ie_u32 frame_rect =
-          wuffs_base__frame_config__bounds(&fc);
-      wuffs_base__rect_ie_u32 dirty_rect =
-          wuffs_gif__decoder__frame_dirty_rect(&dec);
-      if (!wuffs_base__rect_ie_u32__contains_rect(&frame_rect, dirty_rect)) {
-        ret = "internal error: frame_rect does not contain dirty_rect";
-        goto exit;
-      }
-
-      if (!wuffs_base__status__is_ok(&status)) {
-        if ((status.repr != wuffs_base__note__end_of_data) || !seen_ok) {
-          ret = wuffs_base__status__message(&status);
-        }
-        goto exit;
-      }
-      seen_ok = true;
-
-      if (!wuffs_base__rect_ie_u32__equals(&frame_rect, dirty_rect)) {
-        ret = "internal error: frame_rect does not equal dirty_rect";
-        goto exit;
-      }
-    }
+  wuffs_gif__decoder dec;
+  wuffs_base__status status = wuffs_gif__decoder__initialize(
+      &dec, sizeof dec, WUFFS_VERSION,
+      (hash & 1) ? WUFFS_INITIALIZE__LEAVE_INTERNAL_BUFFERS_UNINITIALIZED : 0);
+  hash >>= 1;
+  if (!wuffs_base__status__is_ok(&status)) {
+    return wuffs_base__status__message(&status);
   }
-
-exit:
-  free(workbuf.ptr);
-  free(pixbuf.ptr);
-  return ret;
+  return fuzz_image_decoder(
+      src, hash,
+      wuffs_gif__decoder__upcast_as__wuffs_base__image_decoder(&dec));
 }