std/xz: implement apply_filter_0b_riscv
diff --git a/release/c/wuffs-unsupported-snapshot.c b/release/c/wuffs-unsupported-snapshot.c
index daa3e28..6e9e897 100644
--- a/release/c/wuffs-unsupported-snapshot.c
+++ b/release/c/wuffs-unsupported-snapshot.c
@@ -66847,6 +66847,12 @@
     wuffs_base__slice_u8 a_dst_slice);
 
 WUFFS_BASE__GENERATED_C_CODE
+static uint8_t
+wuffs_xz__decoder__apply_filter_0b_riscv(
+    wuffs_xz__decoder* self,
+    wuffs_base__slice_u8 a_dst_slice);
+
+WUFFS_BASE__GENERATED_C_CODE
 static wuffs_base__status
 wuffs_xz__decoder__do_transform_io(
     wuffs_xz__decoder* self,
@@ -67385,6 +67391,98 @@
   return ((uint8_t)(((uint64_t)(v_s.len))));
 }
 
+// -------- func xz.decoder.apply_filter_0b_riscv
+
+WUFFS_BASE__GENERATED_C_CODE
+static uint8_t
+wuffs_xz__decoder__apply_filter_0b_riscv(
+    wuffs_xz__decoder* self,
+    wuffs_base__slice_u8 a_dst_slice) {
+  wuffs_base__slice_u8 v_s = {0};
+  uint32_t v_p = 0;
+  uint32_t v_x = 0;
+  uint32_t v_x27 = 0;
+  uint32_t v_y = 0;
+  uint32_t v_addr = 0;
+
+  v_s = a_dst_slice;
+  v_p = self->private_impl.f_bcj_pos;
+  while (((uint64_t)(v_s.len)) >= 8u) {
+    if (v_s.ptr[0u] == 239u) {
+      if (((uint8_t)(v_s.ptr[1u] & 13u)) != 0u) {
+        v_p += 2u;
+        v_s = wuffs_base__slice_u8__subslice_i(v_s, 2u);
+        continue;
+      }
+      v_addr = ((((uint32_t)(((uint8_t)(v_s.ptr[1u] & 240u)))) << 13u) | (((uint32_t)(v_s.ptr[2u])) << 9u) | (((uint32_t)(v_s.ptr[3u])) << 1u));
+      v_addr -= v_p;
+      v_s.ptr[1u] = ((uint8_t)(((uint8_t)(v_s.ptr[1u] & 15u)) | ((uint8_t)(((v_addr >> 8u) & 240u)))));
+      v_s.ptr[2u] = ((uint8_t)((((v_addr >> 16u) & 15u) | ((v_addr >> 7u) & 16u) | (((uint32_t)(v_addr << 4u)) & 224u))));
+      v_s.ptr[3u] = ((uint8_t)((((v_addr >> 4u) & 127u) | ((v_addr >> 13u) & 128u))));
+      v_p += 4u;
+      v_s = wuffs_base__slice_u8__subslice_i(v_s, 4u);
+      continue;
+    } else if (((uint8_t)(v_s.ptr[0u] & 127u)) == 23u) {
+      v_x = ((((uint32_t)(v_s.ptr[0u])) << 0u) |
+          (((uint32_t)(v_s.ptr[1u])) << 8u) |
+          (((uint32_t)(v_s.ptr[2u])) << 16u) |
+          (((uint32_t)(v_s.ptr[3u])) << 24u));
+      if ((v_x & 3712u) != 0u) {
+        v_y = ((((uint32_t)(v_s.ptr[4u])) << 0u) |
+            (((uint32_t)(v_s.ptr[5u])) << 8u) |
+            (((uint32_t)(v_s.ptr[6u])) << 16u) |
+            (((uint32_t)(v_s.ptr[7u])) << 24u));
+        if (((((uint32_t)(v_x << 8u)) ^ ((uint32_t)(v_y - 3u))) & 1015811u) != 0u) {
+          v_p += 6u;
+          v_s = wuffs_base__slice_u8__subslice_i(v_s, 6u);
+          continue;
+        }
+        v_addr = ((v_x & 4294963200u) | (v_y >> 20u));
+        v_x = (279u | ((uint32_t)(v_y << 12u)));
+        v_s.ptr[0u] = ((uint8_t)((v_x >> 0u)));
+        v_s.ptr[1u] = ((uint8_t)((v_x >> 8u)));
+        v_s.ptr[2u] = ((uint8_t)((v_x >> 16u)));
+        v_s.ptr[3u] = ((uint8_t)((v_x >> 24u)));
+        v_s.ptr[4u] = ((uint8_t)((v_addr >> 0u)));
+        v_s.ptr[5u] = ((uint8_t)((v_addr >> 8u)));
+        v_s.ptr[6u] = ((uint8_t)((v_addr >> 16u)));
+        v_s.ptr[7u] = ((uint8_t)((v_addr >> 24u)));
+        v_p += 8u;
+        v_s = wuffs_base__slice_u8__subslice_i(v_s, 8u);
+        continue;
+      }
+      v_x27 = (v_x >> 27u);
+      if (((uint32_t)(((uint32_t)(v_x - 12567u)) << 18u)) >= (v_x27 & 29u)) {
+        v_p += 4u;
+        v_s = wuffs_base__slice_u8__subslice_i(v_s, 4u);
+        continue;
+      }
+      v_addr = ((((uint32_t)(v_s.ptr[4u])) << 24u) |
+          (((uint32_t)(v_s.ptr[5u])) << 16u) |
+          (((uint32_t)(v_s.ptr[6u])) << 8u) |
+          (((uint32_t)(v_s.ptr[7u])) << 0u));
+      v_addr -= v_p;
+      v_y = ((v_x >> 12u) | ((uint32_t)(v_addr << 20u)));
+      v_x = (23u | (v_x27 << 7u) | (((uint32_t)(v_addr + 2048u)) & 4294963200u));
+      v_s.ptr[0u] = ((uint8_t)((v_x >> 0u)));
+      v_s.ptr[1u] = ((uint8_t)((v_x >> 8u)));
+      v_s.ptr[2u] = ((uint8_t)((v_x >> 16u)));
+      v_s.ptr[3u] = ((uint8_t)((v_x >> 24u)));
+      v_s.ptr[4u] = ((uint8_t)((v_y >> 0u)));
+      v_s.ptr[5u] = ((uint8_t)((v_y >> 8u)));
+      v_s.ptr[6u] = ((uint8_t)((v_y >> 16u)));
+      v_s.ptr[7u] = ((uint8_t)((v_y >> 24u)));
+      v_p += 8u;
+      v_s = wuffs_base__slice_u8__subslice_i(v_s, 8u);
+      continue;
+    }
+    v_p += 2u;
+    v_s = wuffs_base__slice_u8__subslice_i(v_s, 2u);
+  }
+  self->private_impl.f_bcj_pos = v_p;
+  return ((uint8_t)(((uint64_t)(v_s.len))));
+}
+
 // -------- func xz.decoder.get_quirk
 
 WUFFS_BASE__GENERATED_C_CODE
@@ -68382,7 +68480,7 @@
           self->private_data.f_filter_data[v_f][v_k] = 0u;
           v_k += 1u;
         }
-      } else if ((v_filter_id < 3u) || (10u < v_filter_id)) {
+      } else if ((v_filter_id < 3u) || (11u < v_filter_id)) {
         status = wuffs_base__make_status(wuffs_xz__error__unsupported_filter);
         goto exit;
       } else if (v_f != 0u) {
@@ -68408,9 +68506,12 @@
         } else if (v_filter_id == 9u) {
           self->private_impl.choosy_apply_non_final_filters = (
               &wuffs_xz__decoder__apply_filter_09_sparc);
-        } else {
+        } else if (v_filter_id == 10u) {
           self->private_impl.choosy_apply_non_final_filters = (
               &wuffs_xz__decoder__apply_filter_0a_arm64);
+        } else {
+          self->private_impl.choosy_apply_non_final_filters = (
+              &wuffs_xz__decoder__apply_filter_0b_riscv);
         }
         {
           WUFFS_BASE__COROUTINE_SUSPENSION_POINT(7);
diff --git a/std/xz/decode_filter.wuffs b/std/xz/decode_filter.wuffs
index fbc4dbe..7c560e8 100644
--- a/std/xz/decode_filter.wuffs
+++ b/std/xz/decode_filter.wuffs
@@ -440,3 +440,105 @@
     this.bcj_pos = p
     return s.length() as base.u8
 }
+
+pri func decoder.apply_filter_0b_riscv!(dst_slice: slice base.u8) base.u8 {
+    var s    : slice base.u8
+    var p    : base.u32
+    var x    : base.u32
+    var x27  : base.u32[..= 31]
+    var y    : base.u32
+    var addr : base.u32
+
+    s = args.dst_slice
+    p = this.bcj_pos
+
+    while s.length() >= 8,
+            post s.length() < 8,
+    {
+        if s[0] == 0xEF {
+            // RISC-V JAL instruction.
+            if (s[1] & 0x0D) <> 0x00 {
+                p ~mod+= 2
+                s = s[2 ..]
+                continue
+            }
+            addr = (((s[1] & 0xF0) as base.u32) << 13) |
+                    ((s[2] as base.u32) << 9) |
+                    ((s[3] as base.u32) << 1)
+            addr ~mod-= p
+            s[1] = (s[1] & 0x0F) |
+                    (((addr >> 8) & 0xF0) as base.u8)
+            s[2] = (((addr >> 16) & 0x0F) |
+                    ((addr >> 7) & 0x10) |
+                    ((addr ~mod<< 4) & 0xE0)) as base.u8
+            s[3] = (((addr >> 4) & 0x7F) |
+                    ((addr >> 13) & 0x80)) as base.u8
+            p ~mod+= 4
+            s = s[4 ..]
+            continue
+
+        } else if (s[0] & 0x7F) == 0x17 {
+            // RISC-V AUIPC instruction.
+            x = ((s[0] as base.u32) << 0x00) |
+                    ((s[1] as base.u32) << 0x08) |
+                    ((s[2] as base.u32) << 0x10) |
+                    ((s[3] as base.u32) << 0x18)
+
+            if (x & 0xE80) <> 0 {
+                y = ((s[4] as base.u32) << 0x00) |
+                        ((s[5] as base.u32) << 0x08) |
+                        ((s[6] as base.u32) << 0x10) |
+                        ((s[7] as base.u32) << 0x18)
+                if (((x ~mod<< 8) ^ (y ~mod- 3)) & 0x000F_8003) <> 0 {
+                    p ~mod+= 6
+                    s = s[6 ..]
+                    continue
+                }
+                addr = (x & 0xFFFF_F000) | (y >> 20)
+                x = 0x117 | (y ~mod<< 12)
+                s[0] = ((x >> 0x00) & 0xFF) as base.u8
+                s[1] = ((x >> 0x08) & 0xFF) as base.u8
+                s[2] = ((x >> 0x10) & 0xFF) as base.u8
+                s[3] = ((x >> 0x18) & 0xFF) as base.u8
+                s[4] = ((addr >> 0x00) & 0xFF) as base.u8
+                s[5] = ((addr >> 0x08) & 0xFF) as base.u8
+                s[6] = ((addr >> 0x10) & 0xFF) as base.u8
+                s[7] = ((addr >> 0x18) & 0xFF) as base.u8
+                p ~mod+= 8
+                s = s[8 ..]
+                continue
+            }
+
+            x27 = x >> 27
+            if ((x ~mod- 0x3117) ~mod<< 18) >= (x27 & 0x1D) {
+                p ~mod+= 4
+                s = s[4 ..]
+                continue
+            }
+            addr = ((s[4] as base.u32) << 0x18) |
+                    ((s[5] as base.u32) << 0x10) |
+                    ((s[6] as base.u32) << 0x08) |
+                    ((s[7] as base.u32) << 0x00)
+            addr ~mod-= p
+            y = (x >> 12) | (addr ~mod<< 20)
+            x = 0x17 | (x27 << 7) | ((addr ~mod+ 0x800) & 0xFFFF_F000)
+            s[0] = ((x >> 0x00) & 0xFF) as base.u8
+            s[1] = ((x >> 0x08) & 0xFF) as base.u8
+            s[2] = ((x >> 0x10) & 0xFF) as base.u8
+            s[3] = ((x >> 0x18) & 0xFF) as base.u8
+            s[4] = ((y >> 0x00) & 0xFF) as base.u8
+            s[5] = ((y >> 0x08) & 0xFF) as base.u8
+            s[6] = ((y >> 0x10) & 0xFF) as base.u8
+            s[7] = ((y >> 0x18) & 0xFF) as base.u8
+            p ~mod+= 8
+            s = s[8 ..]
+            continue
+        }
+
+        p ~mod+= 2
+        s = s[2 ..]
+    } endwhile
+
+    this.bcj_pos = p
+    return s.length() as base.u8
+}
diff --git a/std/xz/decode_xz.wuffs b/std/xz/decode_xz.wuffs
index 50bb338..04240e1 100644
--- a/std/xz/decode_xz.wuffs
+++ b/std/xz/decode_xz.wuffs
@@ -413,7 +413,7 @@
                 k += 1
             } endwhile
 
-        } else if (filter_id < 0x03) or (0x0A < filter_id) {
+        } else if (filter_id < 0x03) or (0x0B < filter_id) {
             return "#unsupported filter"
         } else if f <> 0 {
             // We only support BCJ (Branch, Call, Jump) filters if they're the
@@ -434,8 +434,10 @@
                 choose apply_non_final_filters = [apply_filter_08_armthumb]
             } else if filter_id == 0x09 {
                 choose apply_non_final_filters = [apply_filter_09_sparc]
-            } else {
+            } else if filter_id == 0x0A {
                 choose apply_non_final_filters = [apply_filter_0a_arm64]
+            } else {
+                choose apply_non_final_filters = [apply_filter_0b_riscv]
             }
 
             c8 = args.src.read_u8?()
diff --git a/test/3pdata/mzcat-checksums-of-xzsuite.txt b/test/3pdata/mzcat-checksums-of-xzsuite.txt
index 67b30ff..3e39cb5 100644
--- a/test/3pdata/mzcat-checksums-of-xzsuite.txt
+++ b/test/3pdata/mzcat-checksums-of-xzsuite.txt
@@ -62,6 +62,8 @@
 OK. db4847fb test/3pdata/xzsuite/good-1-lzma2-3.xz
 OK. db4847fb test/3pdata/xzsuite/good-1-lzma2-4.xz
 OK. 00000000 test/3pdata/xzsuite/good-1-lzma2-5.xz
+OK. 9d269cdc test/3pdata/xzsuite/good-1-riscv-lzma2-1.xz
+OK. 8d115715 test/3pdata/xzsuite/good-1-riscv-lzma2-2.xz
 OK. cc270da1 test/3pdata/xzsuite/good-1-sparc-lzma2.xz
 OK. a3679091 test/3pdata/xzsuite/good-1-x86-lzma2.xz
 OK. 15a2a343 test/3pdata/xzsuite/good-2-lzma2.xz
diff --git a/test/data/artificial-xz-filter/make.go b/test/data/artificial-xz-filter/make.go
index d81c02a..7355a09 100644
--- a/test/data/artificial-xz-filter/make.go
+++ b/test/data/artificial-xz-filter/make.go
@@ -39,6 +39,9 @@
 		// No test case generated for 04/x86, as the x86 CPU bytecode format is
 		// complicated (variable length instructions). Test coverage is instead
 		// provided by xz-tests-files's good-1-x86-lzma2.xz file.
+		//
+		// Similarly, no test case here for 0b/riscv. It's covered by the
+		// good-1-riscv-lzma2-*.xz files.
 		{"05", "powerpc", genPowerpc},
 		{"06", "ia64", genIa64},
 		{"07", "arm", genArm},