Add io_reader.peek_u64le_at
diff --git a/internal/cgen/builtin.go b/internal/cgen/builtin.go
index 753e758..06cc079 100644
--- a/internal/cgen/builtin.go
+++ b/internal/cgen/builtin.go
@@ -177,6 +177,14 @@
 		b.writeb(')')
 		return nil
 
+	case t.IDPeekU64LEAt:
+		b.printf("wuffs_base__load_u64le__no_bounds_check(%s%s + ", iopPrefix, name)
+		if err := g.writeExpr(b, args[0].AsArg().Value(), depth); err != nil {
+			return err
+		}
+		b.writeb(')')
+		return nil
+
 	case t.IDPosition:
 		b.printf("wuffs_base__u64__sat_add(%s->meta.pos, ((uint64_t)(%s%s - %s%s)))",
 			name, iopPrefix, name, io0Prefix, name)
diff --git a/lang/builtin/builtin.go b/lang/builtin/builtin.go
index 2ec2221..af5503b 100644
--- a/lang/builtin/builtin.go
+++ b/lang/builtin/builtin.go
@@ -248,6 +248,11 @@
 	"io_reader.peek_u64be() u64",
 	"io_reader.peek_u64le() u64",
 
+	// As an implementation restriction, we require that offset has a constant
+	// value. The (0x1_0000 - sizeof(u64)) limit is arbitrary, but high enough
+	// in practice.
+	"io_reader.peek_u64le_at(offset: u32[..= 0xFFF8]) u64",
+
 	"io_reader.available() u64",
 	"io_reader.count_since(mark: u64) u64",
 	"io_reader.is_closed() bool",
diff --git a/lang/check/bounds.go b/lang/check/bounds.go
index 2bdfa85..dfdcc20 100644
--- a/lang/check/bounds.go
+++ b/lang/check/bounds.go
@@ -1068,10 +1068,22 @@
 			} else if err != nil {
 				return bounds{}, err
 			}
-			advance, update = worstCase.ConstValue(), true
-			if advance == nil {
+			if worstCase.ConstValue() == nil {
 				return bounds{}, fmt.Errorf("check: skip_fast worst_case is not a constant value")
 			}
+			advance, update = worstCase.ConstValue(), true
+
+		} else if method == t.IDPeekU64LEAt {
+			args := n.Args()
+			if len(args) != 1 {
+				return bounds{}, fmt.Errorf("check: internal error: bad peek_u64le_at arguments")
+			}
+			offset := args[0].AsArg().Value()
+			if offset.ConstValue() == nil {
+				return bounds{}, fmt.Errorf("check: peek_u64le_at offset is not a constant value")
+			}
+			advance, update = big.NewInt(8), false
+			advance.Add(advance, offset.ConstValue())
 
 		} else if method >= t.IDPeekU8 {
 			if m := method - t.IDPeekU8; m < t.ID(len(ioMethodAdvances)) {
diff --git a/lang/token/list.go b/lang/token/list.go
index ea2e069..94e7767 100644
--- a/lang/token/list.go
+++ b/lang/token/list.go
@@ -544,6 +544,8 @@
 
 	// --------
 
+	IDPeekU64LEAt = ID(0x1A0)
+
 	IDPeekU8 = ID(0x1A1)
 
 	IDPeekU16BE = ID(0x1A2)
@@ -899,6 +901,8 @@
 
 	// --------
 
+	IDPeekU64LEAt: "peek_u64le_at",
+
 	IDPeekU8: "peek_u8",
 
 	IDPeekU16BE: "peek_u16be",
diff --git a/release/c/wuffs-unsupported-snapshot.c b/release/c/wuffs-unsupported-snapshot.c
index 6dce4a1..c19ce80 100644
--- a/release/c/wuffs-unsupported-snapshot.c
+++ b/release/c/wuffs-unsupported-snapshot.c
@@ -20468,7 +20468,6 @@
   uint64_t v_uni4_string = 0;
   uint32_t v_uni4_value = 0;
   uint32_t v_uni4_high_surrogate = 0;
-  uint32_t v_uni4_rollback = 0;
   uint32_t v_expect = 0;
   uint32_t v_expect_after_value = 0;
 
@@ -20791,10 +20790,9 @@
                       v_char = 0;
                       goto label__string_loop_outer__continue;
                     }
-                    (iop_a_src += 4, wuffs_base__make_empty_struct());
-                    v_uni4_string =
-                        (wuffs_base__load_u64le__no_bounds_check(iop_a_src) >>
-                         16);
+                    v_uni4_string = (wuffs_base__load_u64le__no_bounds_check(
+                                         iop_a_src + 4) >>
+                                     16);
                     if (((255 & (v_uni4_string >> 0)) != 92) ||
                         ((255 & (v_uni4_string >> 8)) != 117)) {
                       v_uni4_high_surrogate = 0;
@@ -20826,7 +20824,7 @@
                     if ((v_uni4_ok != 0) && (56320 <= v_uni4_value) &&
                         (v_uni4_value <= 57343)) {
                       v_uni4_value -= 56320;
-                      (iop_a_src += 8, wuffs_base__make_empty_struct());
+                      (iop_a_src += 12, wuffs_base__make_empty_struct());
                       *iop_a_dst++ = wuffs_base__make_token(
                           (((uint64_t)((6291456 | v_uni4_high_surrogate |
                                         v_uni4_value)))
@@ -20835,18 +20833,6 @@
                           (((uint64_t)(12))
                            << WUFFS_BASE__TOKEN__LENGTH__SHIFT));
                       goto label__string_loop_outer__continue;
-                    } else {
-                      v_uni4_rollback = 4;
-                      while (v_uni4_rollback > 0) {
-                        v_uni4_rollback -= 1;
-                        if (iop_a_src > io1_a_src) {
-                          (iop_a_src--, wuffs_base__make_empty_struct());
-                        } else {
-                          status = wuffs_base__make_status(
-                              wuffs_json__error__internal_error_inconsistent_i_o);
-                          goto exit;
-                        }
-                      }
                     }
                   }
                   if (self->private_impl
diff --git a/std/json/decode_json.wuffs b/std/json/decode_json.wuffs
index 0f9dbd7..f24e00a 100644
--- a/std/json/decode_json.wuffs
+++ b/std/json/decode_json.wuffs
@@ -123,7 +123,6 @@
 	var uni4_string         : base.u64
 	var uni4_value          : base.u32[..= 0xFFFF]
 	var uni4_high_surrogate : base.u32[..= 0x10_FC00]
-	var uni4_rollback       : base.u32
 
 	// expect is a bitmask of what the next character class can be.
 	//
@@ -397,7 +396,8 @@
 									// High surrogate, which needs to be
 									// followed by a "\\u1234" low surrogate.
 									// We've already peeked 6 bytes for the
-									// high surrogate. We need another 6.
+									// high surrogate. We need 12 in total:
+									// another 8 bytes at an offset of 4.
 									if args.src.available() < 12 {
 										if args.src.is_closed() {
 											if this.quirk_enabled_replace_invalid_utf_8 {
@@ -417,12 +417,7 @@
 										char = 0
 										continue.string_loop_outer
 									}
-
-									// Roll forward 4 bytes, so that calling
-									// peek_u64le will pick up the last 8.
-									args.src.skip32_fast!(actual: 4, worst_case: 4)
-
-									uni4_string = args.src.peek_u64le() >> 16
+									uni4_string = args.src.peek_u64le_at(offset: 4) >> 16
 
 									// Look for the low surrogate's "\\u".
 									if ((0xFF & (uni4_string >> 0)) <> 0x5C) or
@@ -456,29 +451,13 @@
 
 										// Emit a single token for the surrogate pair.
 										uni4_value -= 0xDC00
-										args.src.skip32_fast!(actual: 8, worst_case: 8)
+										args.src.skip32_fast!(actual: 12, worst_case: 12)
 										args.dst.write_fast_token!(
 											value_major: 0,
 											value_minor: 0x60_0000 | uni4_high_surrogate | uni4_value,
 											link: 0x3,
 											length: 12)
 										continue.string_loop_outer
-
-									} else {
-										// Roll back the 4 bytes, then fall
-										// through to "#invalid
-										// backslash-escape".
-										uni4_rollback = 4
-										while uni4_rollback > 0,
-											inv args.dst.available() > 0,
-										{
-											uni4_rollback -= 1
-											if args.src.can_undo_byte() {
-												args.src.undo_byte!()
-											} else {
-												return "#internal error: inconsistent I/O"
-											}
-										}
 									}
 								}