From 276ab5935873edc05e1882c06fc527a14babd27c Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Mon, 30 Jan 2023 14:50:47 -0700 Subject: encoder, reencoder: Distinguish between syntax errors and I/O errors --- ReleaseNotes.md | 7 +++++++ encode.go | 6 ++++++ errors.go | 24 ++++++++++++++++++++++++ reencode.go | 12 +++++++++--- 4 files changed, 46 insertions(+), 3 deletions(-) diff --git a/ReleaseNotes.md b/ReleaseNotes.md index 9974f0e..e00bf10 100644 --- a/ReleaseNotes.md +++ b/ReleaseNotes.md @@ -8,6 +8,13 @@ a user of lowmemjson's native APIs does not need to import `encoding/json` or compat/json in order to use them. + - Encoder, ReEncoder: If there was an error writing to the output + stream, it may have returned a `*ReEncodeSyntaxError` even though + it's not a syntax issue, or may have returned the underlying + error without wrapping it. If there is an error writing to the + output, Encoder and ReEncoder now return `*EncodeWriteError` and + `*ReEncodeWriteError` respectively. + # v0.3.5 (2023-02-10) Theme: Compatibility bugfixes diff --git a/encode.go b/encode.go index b6541cd..d39c862 100644 --- a/encode.go +++ b/encode.go @@ -83,6 +83,12 @@ func (enc *Encoder) Encode(obj any) (err error) { enc.w.par.Reset() } if err := encode(enc.w, reflect.ValueOf(obj), enc.w.BackslashEscape, false, 0, map[any]struct{}{}); err != nil { + if rwe, ok := err.(*ReEncodeWriteError); ok { + err = &EncodeWriteError{ + Err: rwe.Err, + Offset: rwe.Offset, + } + } return err } if enc.isRoot { diff --git a/errors.go b/errors.go index 0a47db4..516018c 100644 --- a/errors.go +++ b/errors.go @@ -121,6 +121,18 @@ func (e *DecodeError) Unwrap() error { return e.Err } // encode errors /////////////////////////////////////////////////////////////////////////////////// +// A EncodeWriteError is returned from Encode if there is an error +// writing to the output stream. +type EncodeWriteError struct { + Err error + Offset int64 +} + +func (e *EncodeWriteError) Error() string { + return fmt.Sprintf("json: I/O error at output byte %v: %v", e.Offset, e.Err) +} +func (e *EncodeWriteError) Unwrap() error { return e.Err } + // An EncodeTypeError is returned by Encode when attempting to encode // an unsupported type. // @@ -155,6 +167,18 @@ func (e *EncodeMethodError) Unwrap() error { return e.Err } // reencode errors ///////////////////////////////////////////////////////////////////////////////// +// A ReEncodeWriteError is returned from ReEncoder's methods if there +// is an error writing to the output stream. +type ReEncodeWriteError struct { + Err error + Offset int64 +} + +func (e *ReEncodeWriteError) Error() string { + return fmt.Sprintf("json: I/O error at output byte %v: %v", e.Offset, e.Err) +} +func (e *ReEncodeWriteError) Unwrap() error { return e.Err } + // A ReEncodeSyntaxError is returned from ReEncoder's methods if there // is a syntax error in the input. type ReEncodeSyntaxError struct { diff --git a/reencode.go b/reencode.go index e48c58c..d19dc1a 100644 --- a/reencode.go +++ b/reencode.go @@ -232,7 +232,7 @@ func (enc *ReEncoder) Close() error { } if len(enc.barriers) == 0 { if err := enc.handleRune(0, jsonparse.RuneTypeError, enc.stackSize()); err != nil { - enc.err = &ReEncodeSyntaxError{ + enc.err = &ReEncodeWriteError{ Err: err, Offset: enc.inputPos, } @@ -274,8 +274,14 @@ rehandle: } return enc.written, enc.err } - enc.err = enc.handleRune(c, t, enc.stackSize()) - if enc.err == nil && t == jsonparse.RuneTypeEOF { + if err := enc.handleRune(c, t, enc.stackSize()); err != nil { + enc.err = &ReEncodeWriteError{ + Err: err, + Offset: enc.inputPos, + } + return enc.written, enc.err + } + if t == jsonparse.RuneTypeEOF { if enc.AllowMultipleValues && len(enc.barriers) == 0 { enc.par.Reset() goto rehandle -- cgit v1.2.3-2-g168b