Skip to content

Add error to Decoder.PeekKind and add Decoder.More #146

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions arshal_any.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,11 @@ func marshalValueAny(enc *jsontext.Encoder, val any, mo *jsonopts.Struct) error
// for any possible nested value.
// Duplicate names must be rejected since this does not implement merging.
func unmarshalValueAny(dec *jsontext.Decoder, uo *jsonopts.Struct) (any, error) {
switch k := dec.PeekKind(); k {
k, err := dec.PeekKind()
if err != nil {
return nil, err
}
switch k {
case '{':
return unmarshalObjectAny(dec, uo)
case '[':
Expand Down Expand Up @@ -180,7 +184,7 @@ func unmarshalObjectAny(dec *jsontext.Decoder, uo *jsonopts.Struct) (map[string]
export.Decoder(dec).Tokens.Last.DisableNamespace()
}
var errUnmarshal error
for dec.PeekKind() != '}' {
for dec.More() {
tok, err := dec.ReadToken()
if err != nil {
return obj, err
Expand Down Expand Up @@ -264,7 +268,7 @@ func unmarshalArrayAny(dec *jsontext.Decoder, uo *jsonopts.Struct) ([]any, error
}
arr := []any{}
var errUnmarshal error
for dec.PeekKind() != ']' {
for dec.More() {
val, err := unmarshalValueAny(dec, uo)
arr = append(arr, val)
if err != nil {
Expand Down
30 changes: 20 additions & 10 deletions arshal_default.go
Original file line number Diff line number Diff line change
Expand Up @@ -363,7 +363,7 @@ func makeBytesArshaler(t reflect.Type, fncs *arshaler) *arshaler {
return newInvalidFormatError(dec, t, uo)
}
} else if uo.Flags.Get(jsonflags.FormatBytesWithLegacySemantics) &&
(va.Kind() == reflect.Array || dec.PeekKind() == '[') {
(va.Kind() == reflect.Array || xd.PeekKind() == '[') {
return unmarshalArray(dec, va, uo)
}
var flags jsonwire.ValueFlags
Expand Down Expand Up @@ -926,7 +926,7 @@ func makeMapArshaler(t reflect.Type) *arshaler {
}

var errUnmarshal error
for dec.PeekKind() != '}' {
for dec.More() {
// Unmarshal the map entry key.
k.SetZero()
err := unmarshalKey(dec, k, uo)
Expand Down Expand Up @@ -1214,7 +1214,7 @@ func makeStructArshaler(t reflect.Type) *arshaler {
var seenIdxs uintSet
xd.Tokens.Last.DisableNamespace()
var errUnmarshal error
for dec.PeekKind() != '}' {
for dec.More() {
// Process the object member name.
var flags jsonwire.ValueFlags
val, err := xd.ReadValue(&flags)
Expand Down Expand Up @@ -1482,7 +1482,7 @@ func makeSliceArshaler(t reflect.Type) *arshaler {
}
var i int
var errUnmarshal error
for dec.PeekKind() != ']' {
for dec.More() {
if i == cap {
va.Value.Grow(1)
cap = va.Cap()
Expand Down Expand Up @@ -1578,7 +1578,7 @@ func makeArrayArshaler(t reflect.Type) *arshaler {
}
var i int
var errUnmarshal error
for dec.PeekKind() != ']' {
for dec.More() {
if i >= n {
if err := dec.SkipValue(); err != nil {
return err
Expand Down Expand Up @@ -1648,7 +1648,10 @@ func makePointerArshaler(t reflect.Type) *arshaler {
}
fncs.unmarshal = func(dec *jsontext.Decoder, va addressableValue, uo *jsonopts.Struct) error {
// NOTE: Struct.Format is forwarded to underlying unmarshal.
if dec.PeekKind() == 'n' {
switch k, err := dec.PeekKind(); {
case err != nil:
return err
case k == 'n':
if _, err := dec.ReadToken(); err != nil {
return err
}
Expand Down Expand Up @@ -1744,6 +1747,10 @@ func makeInterfaceArshaler(t reflect.Type) *arshaler {
if uo.Format != "" && uo.FormatDepth == xd.Tokens.Depth() {
return newInvalidFormatError(dec, t, uo)
}
k, err := dec.PeekKind()
if err != nil {
return err
}
if uo.Flags.Get(jsonflags.MergeWithLegacySemantics) && !va.IsNil() {
// Legacy merge behavior is difficult to explain.
// In general, it only merges for non-nil pointer kinds.
Expand All @@ -1752,7 +1759,7 @@ func makeInterfaceArshaler(t reflect.Type) *arshaler {
// (rather than setting the interface value itself to nil).
e := va.Elem()
if e.Kind() == reflect.Pointer && !e.IsNil() {
if dec.PeekKind() == 'n' && e.Elem().Kind() == reflect.Pointer {
if k == 'n' && e.Elem().Kind() == reflect.Pointer {
if _, err := dec.ReadToken(); err != nil {
return err
}
Expand All @@ -1763,7 +1770,7 @@ func makeInterfaceArshaler(t reflect.Type) *arshaler {
va.SetZero()
}
}
if dec.PeekKind() == 'n' {
if k == 'n' {
if _, err := dec.ReadToken(); err != nil {
return err
}
Expand All @@ -1789,7 +1796,10 @@ func makeInterfaceArshaler(t reflect.Type) *arshaler {
return err
}

k := dec.PeekKind()
k, err := dec.PeekKind()
if err != nil {
return err
}
if !isAnyType(t) {
return newUnmarshalErrorBeforeWithSkipping(dec, uo, t, errNilInterface)
}
Expand Down Expand Up @@ -1827,7 +1837,7 @@ func makeInterfaceArshaler(t reflect.Type) *arshaler {
if uo.Unmarshalers != nil {
unmarshal, _ = uo.Unmarshalers.(*Unmarshalers).lookup(unmarshal, v.Type())
}
err := unmarshal(dec, v, uo)
err = unmarshal(dec, v, uo)
va.Set(v.Value)
return err
}
Expand Down
2 changes: 1 addition & 1 deletion arshal_inlined.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ func marshalInlinedFallbackAll(enc *jsontext.Encoder, va addressableValue, mo *j
if tok.Kind() != '{' {
return newMarshalErrorBefore(enc, v.Type(), errRawInlinedNotObject)
}
for dec.PeekKind() != '}' {
for dec.More() {
// Parse the JSON object name.
var flags jsonwire.ValueFlags
val, err := xd.ReadValue(&flags)
Expand Down
2 changes: 1 addition & 1 deletion arshal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7138,7 +7138,7 @@ func TestUnmarshal(t *testing.T) {
name: jsontest.Name("Structs/Invalid/NestedErrUnexpectedEOF"),
inBuf: `{"Pointer":`,
inVal: addr(structAll{}),
want: addr(structAll{Pointer: new(structAll)}),
want: addr(structAll{}),
wantErr: &jsontext.SyntacticError{ByteOffset: len64(`{"Pointer":`), JSONPointer: "/Pointer", Err: io.ErrUnexpectedEOF},
}, {
name: jsontest.Name("Structs/Invalid/Conflicting"),
Expand Down
2 changes: 1 addition & 1 deletion bench_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -563,7 +563,7 @@ func runSlowStreamingDecode(t testing.TB, typeName string, data []byte) {
dec := jsontext.NewDecoder(iotest.OneByteReader(bytes.NewReader(data)))
switch typeName {
case "Token":
for dec.PeekKind() > 0 {
for dec.More() {
if _, err := dec.ReadToken(); err != nil {
t.Fatalf("Decoder.ReadToken error: %v", err)
}
Expand Down
10 changes: 5 additions & 5 deletions example_orderedobject_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,13 @@ func (obj *OrderedObject[V]) MarshalJSONTo(enc *jsontext.Encoder, opts json.Opti

// UnmarshalJSONFrom decodes a JSON object from dec into obj.
func (obj *OrderedObject[V]) UnmarshalJSONFrom(dec *jsontext.Decoder, opts json.Options) error {
if k := dec.PeekKind(); k != '{' {
return fmt.Errorf("expected object start, but encountered %v", k)
}
if _, err := dec.ReadToken(); err != nil {
switch tok, err := dec.ReadToken(); {
case err != nil:
return err
case tok.Kind() != '{':
return fmt.Errorf("expected object start, but encountered %v", tok.Kind())
}
for dec.PeekKind() != '}' {
for dec.More() {
*obj = append(*obj, ObjectMember[V]{})
member := &(*obj)[len(*obj)-1]
if err := json.UnmarshalDecode(dec, &member.Name, opts); err != nil {
Expand Down
5 changes: 4 additions & 1 deletion example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -607,7 +607,10 @@ func ExampleWithUnmarshalers_rawNumber() {
json.UnmarshalFromFunc(func(dec *jsontext.Decoder, val *any, opts json.Options) error {
// If the next value to be decoded is a JSON number,
// then provide a concrete Go type to unmarshal into.
if dec.PeekKind() == '0' {
switch k, err := dec.PeekKind(); {
case err != nil:
return err
case k == '0':
*val = jsontext.Value(nil)
}
// Return SkipFunc to fallback on default unmarshal behavior.
Expand Down
1 change: 1 addition & 0 deletions inline_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ func TestInline(t *testing.T) {
},
"./jsontext": {
"encoderState.NeedFlush": true,
"Decoder.More": true,
"Decoder.ReadToken": true, // thin wrapper over decoderState.ReadToken
"Decoder.ReadValue": true, // thin wrapper over decoderState.ReadValue
"Encoder.WriteToken": true, // thin wrapper over encoderState.WriteToken
Expand Down
6 changes: 3 additions & 3 deletions jsontext/coder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -458,7 +458,7 @@ func testCoderInterleaved(t *testing.T, where jsontest.CasePos, modeName string,
tickTock := modeName == "TokenFirst"
for {
if modeName == "TokenDelims" {
switch dec.PeekKind() {
switch dec.s.PeekKind() {
case '{', '}', '[', ']':
tickTock = true // as token
default:
Expand All @@ -482,7 +482,7 @@ func testCoderInterleaved(t *testing.T, where jsontest.CasePos, modeName string,
// It is a syntactic error to call ReadValue
// at the end of an object or array.
// Retry as a ReadToken call.
expectError := dec.PeekKind() == '}' || dec.PeekKind() == ']'
expectError := dec.s.PeekKind() == '}' || dec.s.PeekKind() == ']'
if expectError {
if !errors.As(err, new(*SyntacticError)) {
t.Fatalf("%s: Decoder.ReadToken error is %T, want %T", where, err, new(SyntacticError))
Expand Down Expand Up @@ -707,7 +707,7 @@ func TestCoderMaxDepth(t *testing.T) {
checkReadToken(t, '{', nil)
checkReadToken(t, '"', nil)
}
checkReadToken(t, 0, wantErr)
checkReadToken(t, invalidKind, wantErr)
})
})

Expand Down
24 changes: 21 additions & 3 deletions jsontext/decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -285,10 +285,18 @@ func (d *decodeBuffer) PreviousTokenOrValue() []byte {
}

// PeekKind retrieves the next token kind, but does not advance the read offset.
// It returns 0 if there are no more tokens.
func (d *Decoder) PeekKind() Kind {
return d.s.PeekKind()
func (d *Decoder) PeekKind() (k Kind, err error) {
k = d.s.PeekKind()
if err = d.s.peekErr; err != nil {
d.s.peekPos, d.s.peekErr = 0, nil // clear the cached error
}
return k, err
}

// PeekKind is like Decoder.PeekKind, but stores an out-of-band error,
// which can retrieved by a call to PeekKind, ReadToken, or ReadValue.
// An error-less return allows for ergonomic iteration with Decoder.More.
// It returns 0 only for [io.EOF].
func (d *decoderState) PeekKind() Kind {
// Check whether we have a cached peek result.
if d.peekPos > 0 {
Expand All @@ -307,6 +315,9 @@ func (d *decoderState) PeekKind() Kind {
err = io.EOF // EOF possibly if no Tokens present after top-level value
}
d.peekPos, d.peekErr = -1, wrapSyntacticError(d, err, pos, 0)
if err == io.EOF {
return 0
}
return invalidKind
}
}
Expand Down Expand Up @@ -339,6 +350,13 @@ func (d *decoderState) PeekKind() Kind {
return next
}

// More reports whether there is another element in the
// current array or object being parsed.
func (d *Decoder) More() bool {
k := d.s.PeekKind()
return k > 0 && k != ']' && k != '}'
}

// checkDelimBeforeIOError checks whether the delim is even valid
// before returning an IO error, which occurs after the delim.
func (d *decoderState) checkDelimBeforeIOError(delim byte, err error) error {
Expand Down
Loading
Loading