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"
- }
- }
}
}