diff --git a/src/encoding/json/v2_stream.go b/src/encoding/json/v2_stream.go
index 28e72c0..37569da 100644
--- a/src/encoding/json/v2_stream.go
+++ b/src/encoding/json/v2_stream.go
@@ -20,6 +20,10 @@
dec *jsontext.Decoder
opts jsonv2.Options
err error
+
+ // hadPeeked reports whether [Decoder.More] was called.
+ // It is reset by [Decoder.Decode] and [Decoder.Token].
+ hadPeeked bool
}
// NewDecoder returns a new decoder that reads from r.
@@ -66,6 +70,7 @@
if dec.err != nil {
return dec.err
}
+ dec.hadPeeked = false
b, err := dec.dec.ReadValue()
if err != nil {
dec.err = transformSyntacticError(err)
@@ -192,6 +197,7 @@
// to mark the start and end of arrays and objects.
// Commas and colons are elided.
func (dec *Decoder) Token() (Token, error) {
+ dec.hadPeeked = false
tok, err := dec.dec.ReadToken()
if err != nil {
// Historically, v1 would report just [io.EOF] if
@@ -230,6 +236,7 @@
// More reports whether there is another element in the
// current array or object being parsed.
func (dec *Decoder) More() bool {
+ dec.hadPeeked = true
k := dec.dec.PeekKind()
return k > 0 && k != ']' && k != '}'
}
@@ -238,5 +245,18 @@
// The offset gives the location of the end of the most recently returned token
// and the beginning of the next token.
func (dec *Decoder) InputOffset() int64 {
- return dec.dec.InputOffset()
+ offset := dec.dec.InputOffset()
+ if dec.hadPeeked {
+ // Historically, InputOffset reported the location of
+ // the end of the most recently returned token
+ // unless [Decoder.More] is called, in which case, it reported
+ // the beginning of the next token.
+ unreadBuffer := dec.dec.UnreadBuffer()
+ trailingTokens := bytes.TrimLeft(unreadBuffer, " \n\r\t")
+ if len(trailingTokens) > 0 {
+ leadingWhitespace := len(unreadBuffer) - len(trailingTokens)
+ offset += int64(leadingWhitespace)
+ }
+ }
+ return offset
}
diff --git a/src/encoding/json/v2_stream_test.go b/src/encoding/json/v2_stream_test.go
index b8951f8..26aa85d 100644
--- a/src/encoding/json/v2_stream_test.go
+++ b/src/encoding/json/v2_stream_test.go
@@ -537,3 +537,64 @@
}
}
}
+
+func TestDecoderInputOffset(t *testing.T) {
+ const input = ` [ [ ] , [ "one" ] , [ "one" , "two" ] , { } , { "alpha" : "bravo" } , { "alpha" : "bravo" , "fizz" : "buzz" } ] `
+ wantOffsets := []int64{
+ 0, 1, 2, 3, 4, 5, 6, 7, 10, 11, 16, 17, 18, 19, 22, 23, 28, 29,
+ 36, 37, 38, 39, 42, 43, 44, 45, 48, 49, 56, 57, 66, 67, 68, 69,
+ 72, 73, 80, 81, 90, 91, 99, 100, 108, 109, 110, 111, 112, 112,
+ 112, 112,
+ }
+ wantMores := []bool{
+ true, true, false, true, true, false, true, true, true, false,
+ true, false, true, true, true, false, true, true, true, true,
+ true, false, false, false, false,
+ }
+
+ d := NewDecoder(strings.NewReader(input))
+ checkOffset := func() {
+ t.Helper()
+ got := d.InputOffset()
+ if len(wantOffsets) == 0 {
+ t.Fatalf("InputOffset = %d, want nil", got)
+ }
+ want := wantOffsets[0]
+ if got != want {
+ t.Fatalf("InputOffset = %d, want %d", got, want)
+ }
+ wantOffsets = wantOffsets[1:]
+ }
+ checkMore := func() {
+ t.Helper()
+ got := d.More()
+ if len(wantMores) == 0 {
+ t.Fatalf("More = %v, want nil", got)
+ }
+ want := wantMores[0]
+ if got != want {
+ t.Fatalf("More = %v, want %v", got, want)
+ }
+ wantMores = wantMores[1:]
+ }
+ checkOffset()
+ checkMore()
+ checkOffset()
+ for {
+ if _, err := d.Token(); err == io.EOF {
+ break
+ } else if err != nil {
+ t.Fatalf("Token error: %v", err)
+ }
+ checkOffset()
+ checkMore()
+ checkOffset()
+ }
+ checkOffset()
+ checkMore()
+ checkOffset()
+
+ if len(wantOffsets)+len(wantMores) > 0 {
+ t.Fatal("unconsumed testdata")
+ }
+}