std/xz: add SHA-256 checksum support
diff --git a/internal/cgen/base/fundamental-public.h b/internal/cgen/base/fundamental-public.h
index 07593a2..6cfe4ce 100644
--- a/internal/cgen/base/fundamental-public.h
+++ b/internal/cgen/base/fundamental-public.h
@@ -892,6 +892,11 @@
   return res;
 }
 
+static inline uint64_t  //
+wuffs_base__bitvec256__get(const wuffs_base__bitvec256* b, uint32_t i) {
+  return b->elements_u64[i & 3];
+}
+
 // --------
 
 // wuffs_base__optional_u63 is like a std::optional<uint64_t>, but for C (not
diff --git a/lang/builtin/builtin.go b/lang/builtin/builtin.go
index 704d8f0..9ca3b7e 100644
--- a/lang/builtin/builtin.go
+++ b/lang/builtin/builtin.go
@@ -367,6 +367,8 @@
 	"u64.max(no_less_than: u64) u64",
 	"u64.min(no_more_than: u64) u64",
 
+	"bitvec256.get(i: u32[..=3]) u64",
+
 	// ---- utility
 
 	"utility.cpu_arch_is_32_bit() bool",
diff --git a/release/c/wuffs-unsupported-snapshot.c b/release/c/wuffs-unsupported-snapshot.c
index e251a06..c50939d 100644
--- a/release/c/wuffs-unsupported-snapshot.c
+++ b/release/c/wuffs-unsupported-snapshot.c
@@ -1164,6 +1164,11 @@
   return res;
 }
 
+static inline uint64_t  //
+wuffs_base__bitvec256__get(const wuffs_base__bitvec256* b, uint32_t i) {
+  return b->elements_u64[i & 3];
+}
+
 // --------
 
 // wuffs_base__optional_u63 is like a std::optional<uint64_t>, but for C (not
@@ -13929,6 +13934,7 @@
 
     struct {
       uint32_t v_checksum32_have;
+      wuffs_base__bitvec256 v_checksum256_have;
       uint32_t v_padding;
       uint64_t scratch;
     } s_do_transform_io;
@@ -66851,6 +66857,7 @@
   uint32_t v_checksum32_want = 0;
   uint64_t v_checksum64_have = 0;
   uint64_t v_checksum64_want = 0;
+  wuffs_base__bitvec256 v_checksum256_have = {0};
   uint32_t v_padding = 0;
   uint8_t v_c8 = 0;
 
@@ -66881,6 +66888,7 @@
   uint32_t coro_susp_point = self->private_impl.p_do_transform_io;
   if (coro_susp_point) {
     v_checksum32_have = self->private_data.s_do_transform_io.v_checksum32_have;
+    v_checksum256_have = self->private_data.s_do_transform_io.v_checksum256_have;
     v_padding = self->private_data.s_do_transform_io.v_padding;
   }
   switch (coro_susp_point) {
@@ -67168,8 +67176,139 @@
           goto exit;
         }
       } else if (self->private_impl.f_checksummer == 3u) {
-        status = wuffs_base__make_status(wuffs_xz__error__todo);
-        goto exit;
+        v_checksum256_have = wuffs_sha256__hasher__checksum_bitvec256(&self->private_data.f_sha256);
+        {
+          WUFFS_BASE__COROUTINE_SUSPENSION_POINT(15);
+          uint64_t t_8;
+          if (WUFFS_BASE__LIKELY(io2_a_src - iop_a_src >= 8)) {
+            t_8 = wuffs_base__peek_u64be__no_bounds_check(iop_a_src);
+            iop_a_src += 8;
+          } else {
+            self->private_data.s_do_transform_io.scratch = 0;
+            WUFFS_BASE__COROUTINE_SUSPENSION_POINT(16);
+            while (true) {
+              if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
+                status = wuffs_base__make_status(wuffs_base__suspension__short_read);
+                goto suspend;
+              }
+              uint64_t* scratch = &self->private_data.s_do_transform_io.scratch;
+              uint32_t num_bits_8 = ((uint32_t)(*scratch & 0xFFu));
+              *scratch >>= 8;
+              *scratch <<= 8;
+              *scratch |= ((uint64_t)(*iop_a_src++)) << (56 - num_bits_8);
+              if (num_bits_8 == 56) {
+                t_8 = ((uint64_t)(*scratch >> 0));
+                break;
+              }
+              num_bits_8 += 8u;
+              *scratch |= ((uint64_t)(num_bits_8));
+            }
+          }
+          v_checksum64_want = t_8;
+        }
+        if (wuffs_base__bitvec256__get(&v_checksum256_have, 3u) != v_checksum64_want) {
+          status = wuffs_base__make_status(wuffs_xz__error__bad_checksum);
+          goto exit;
+        }
+        {
+          WUFFS_BASE__COROUTINE_SUSPENSION_POINT(17);
+          uint64_t t_9;
+          if (WUFFS_BASE__LIKELY(io2_a_src - iop_a_src >= 8)) {
+            t_9 = wuffs_base__peek_u64be__no_bounds_check(iop_a_src);
+            iop_a_src += 8;
+          } else {
+            self->private_data.s_do_transform_io.scratch = 0;
+            WUFFS_BASE__COROUTINE_SUSPENSION_POINT(18);
+            while (true) {
+              if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
+                status = wuffs_base__make_status(wuffs_base__suspension__short_read);
+                goto suspend;
+              }
+              uint64_t* scratch = &self->private_data.s_do_transform_io.scratch;
+              uint32_t num_bits_9 = ((uint32_t)(*scratch & 0xFFu));
+              *scratch >>= 8;
+              *scratch <<= 8;
+              *scratch |= ((uint64_t)(*iop_a_src++)) << (56 - num_bits_9);
+              if (num_bits_9 == 56) {
+                t_9 = ((uint64_t)(*scratch >> 0));
+                break;
+              }
+              num_bits_9 += 8u;
+              *scratch |= ((uint64_t)(num_bits_9));
+            }
+          }
+          v_checksum64_want = t_9;
+        }
+        if (wuffs_base__bitvec256__get(&v_checksum256_have, 2u) != v_checksum64_want) {
+          status = wuffs_base__make_status(wuffs_xz__error__bad_checksum);
+          goto exit;
+        }
+        {
+          WUFFS_BASE__COROUTINE_SUSPENSION_POINT(19);
+          uint64_t t_10;
+          if (WUFFS_BASE__LIKELY(io2_a_src - iop_a_src >= 8)) {
+            t_10 = wuffs_base__peek_u64be__no_bounds_check(iop_a_src);
+            iop_a_src += 8;
+          } else {
+            self->private_data.s_do_transform_io.scratch = 0;
+            WUFFS_BASE__COROUTINE_SUSPENSION_POINT(20);
+            while (true) {
+              if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
+                status = wuffs_base__make_status(wuffs_base__suspension__short_read);
+                goto suspend;
+              }
+              uint64_t* scratch = &self->private_data.s_do_transform_io.scratch;
+              uint32_t num_bits_10 = ((uint32_t)(*scratch & 0xFFu));
+              *scratch >>= 8;
+              *scratch <<= 8;
+              *scratch |= ((uint64_t)(*iop_a_src++)) << (56 - num_bits_10);
+              if (num_bits_10 == 56) {
+                t_10 = ((uint64_t)(*scratch >> 0));
+                break;
+              }
+              num_bits_10 += 8u;
+              *scratch |= ((uint64_t)(num_bits_10));
+            }
+          }
+          v_checksum64_want = t_10;
+        }
+        if (wuffs_base__bitvec256__get(&v_checksum256_have, 1u) != v_checksum64_want) {
+          status = wuffs_base__make_status(wuffs_xz__error__bad_checksum);
+          goto exit;
+        }
+        {
+          WUFFS_BASE__COROUTINE_SUSPENSION_POINT(21);
+          uint64_t t_11;
+          if (WUFFS_BASE__LIKELY(io2_a_src - iop_a_src >= 8)) {
+            t_11 = wuffs_base__peek_u64be__no_bounds_check(iop_a_src);
+            iop_a_src += 8;
+          } else {
+            self->private_data.s_do_transform_io.scratch = 0;
+            WUFFS_BASE__COROUTINE_SUSPENSION_POINT(22);
+            while (true) {
+              if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
+                status = wuffs_base__make_status(wuffs_base__suspension__short_read);
+                goto suspend;
+              }
+              uint64_t* scratch = &self->private_data.s_do_transform_io.scratch;
+              uint32_t num_bits_11 = ((uint32_t)(*scratch & 0xFFu));
+              *scratch >>= 8;
+              *scratch <<= 8;
+              *scratch |= ((uint64_t)(*iop_a_src++)) << (56 - num_bits_11);
+              if (num_bits_11 == 56) {
+                t_11 = ((uint64_t)(*scratch >> 0));
+                break;
+              }
+              num_bits_11 += 8u;
+              *scratch |= ((uint64_t)(num_bits_11));
+            }
+          }
+          v_checksum64_want = t_11;
+        }
+        if (wuffs_base__bitvec256__get(&v_checksum256_have, 0u) != v_checksum64_want) {
+          status = wuffs_base__make_status(wuffs_xz__error__bad_checksum);
+          goto exit;
+        }
       }
     }
 
@@ -67182,6 +67321,7 @@
   suspend:
   self->private_impl.p_do_transform_io = wuffs_base__status__is_suspension(&status) ? coro_susp_point : 0;
   self->private_data.s_do_transform_io.v_checksum32_have = v_checksum32_have;
+  self->private_data.s_do_transform_io.v_checksum256_have = v_checksum256_have;
   self->private_data.s_do_transform_io.v_padding = v_padding;
 
   goto exit;
diff --git a/std/xz/decode_xz.wuffs b/std/xz/decode_xz.wuffs
index b061075..909718f 100644
--- a/std/xz/decode_xz.wuffs
+++ b/std/xz/decode_xz.wuffs
@@ -94,16 +94,17 @@
 }
 
 pri func decoder.do_transform_io?(dst: base.io_writer, src: base.io_reader, workbuf: slice base.u8) {
-    var magic           : base.u64
-    var dmark           : base.u64
-    var smark           : base.u64
-    var status          : base.status
-    var checksum32_have : base.u32
-    var checksum32_want : base.u32
-    var checksum64_have : base.u64
-    var checksum64_want : base.u64
-    var padding         : base.u32
-    var c8              : base.u8
+    var magic            : base.u64
+    var dmark            : base.u64
+    var smark            : base.u64
+    var status           : base.status
+    var checksum32_have  : base.u32
+    var checksum32_want  : base.u32
+    var checksum64_have  : base.u64
+    var checksum64_want  : base.u64
+    var checksum256_have : base.bitvec256
+    var padding          : base.u32
+    var c8               : base.u8
 
     magic = args.src.read_u48le_as_u64?()
     if magic <> '\xFD\x37\x7A\x58\x5A\x00'le {
@@ -215,7 +216,23 @@
                 return "#bad checksum"
             }
         } else if this.checksummer == 3 {
-            return "#TODO"
+            checksum256_have = this.sha256.checksum_bitvec256()
+            checksum64_want = args.src.read_u64be?()
+            if checksum256_have.get(i: 3) <> checksum64_want {
+                return "#bad checksum"
+            }
+            checksum64_want = args.src.read_u64be?()
+            if checksum256_have.get(i: 2) <> checksum64_want {
+                return "#bad checksum"
+            }
+            checksum64_want = args.src.read_u64be?()
+            if checksum256_have.get(i: 1) <> checksum64_want {
+                return "#bad checksum"
+            }
+            checksum64_want = args.src.read_u64be?()
+            if checksum256_have.get(i: 0) <> checksum64_want {
+                return "#bad checksum"
+            }
         }
     } endwhile
 }