diff --git a/decode_float.go b/decode_float.go index cda8ec5..6c2a390 100644 --- a/decode_float.go +++ b/decode_float.go @@ -140,7 +140,7 @@ func (d *floatDecoder) decodeStream(s *stream, depth int64, p unsafe.Pointer) er str := *(*string)(unsafe.Pointer(&bytes)) f64, err := strconv.ParseFloat(str, 64) if err != nil { - return &SyntaxError{msg: err.Error(), Offset: s.totalOffset()} + return errSyntax(err.Error(), s.totalOffset()) } d.op(p, f64) return nil @@ -161,7 +161,7 @@ func (d *floatDecoder) decode(buf []byte, cursor, depth int64, p unsafe.Pointer) s := *(*string)(unsafe.Pointer(&bytes)) f64, err := strconv.ParseFloat(s, 64) if err != nil { - return 0, &SyntaxError{msg: err.Error(), Offset: cursor} + return 0, errSyntax(err.Error(), cursor) } d.op(p, f64) return cursor, nil diff --git a/decode_number.go b/decode_number.go index c667161..07022f8 100644 --- a/decode_number.go +++ b/decode_number.go @@ -27,7 +27,7 @@ func (d *numberDecoder) decodeStream(s *stream, depth int64, p unsafe.Pointer) e return err } if _, err := strconv.ParseFloat(*(*string)(unsafe.Pointer(&bytes)), 64); err != nil { - return &SyntaxError{msg: err.Error(), Offset: s.totalOffset()} + return errSyntax(err.Error(), s.totalOffset()) } d.op(p, Number(string(bytes))) s.reset() @@ -40,7 +40,7 @@ func (d *numberDecoder) decode(buf []byte, cursor, depth int64, p unsafe.Pointer return 0, err } if _, err := strconv.ParseFloat(*(*string)(unsafe.Pointer(&bytes)), 64); err != nil { - return 0, &SyntaxError{msg: err.Error(), Offset: c} + return 0, errSyntax(err.Error(), c) } cursor = c s := *(*string)(unsafe.Pointer(&bytes)) diff --git a/encode_vm.go b/encode_vm.go index 0488be3..950e5a3 100644 --- a/encode_vm.go +++ b/encode_vm.go @@ -61,7 +61,7 @@ func errUnsupportedFloat(v float64) *UnsupportedValueError { } } -func errMarshaler(code *opcode, err error) *MarshalerError { +func errMarshalerWithCode(code *opcode, err error) *MarshalerError { return &MarshalerError{ Type: rtype2type(code.typ), Err: err, diff --git a/encode_vm_escaped_indent.go b/encode_vm_escaped_indent.go index 64b15b6..7776930 100644 --- a/encode_vm_escaped_indent.go +++ b/encode_vm_escaped_indent.go @@ -2,11 +2,9 @@ package json import ( "bytes" - "encoding" "fmt" "math" "reflect" - "runtime" "sort" "strings" "unsafe" @@ -142,65 +140,32 @@ func encodeRunEscapedIndent(ctx *encodeRuntimeContext, b []byte, codeSet *opcode b = bb code = code.next case opMarshalJSON: - ptr := load(ctxptr, code.idx) - if ptr == 0 { + p := load(ctxptr, code.idx) + if p == 0 { b = encodeNull(b) b = encodeIndentComma(b) code = code.next break } - v := ptrToInterface(code, ptr) - bb, err := v.(Marshaler).MarshalJSON() + bb, err := encodeMarshalJSONIndent(ctx, code, b, ptrToInterface(code, p), code.indent, true) if err != nil { - return nil, errMarshaler(code, err) - } - runtime.KeepAlive(v) - if len(bb) == 0 { - return nil, errUnexpectedEndOfJSON( - fmt.Sprintf("error calling MarshalJSON for type %s", code.typ), - 0, - ) - } - var compactBuf bytes.Buffer - if err := compact(&compactBuf, bb, true); err != nil { return nil, err } - var indentBuf bytes.Buffer - if err := encodeWithIndent( - &indentBuf, - compactBuf.Bytes(), - string(ctx.prefix)+strings.Repeat(string(ctx.indentStr), ctx.baseIndent+code.indent), - string(ctx.indentStr), - ); err != nil { - return nil, err - } - b = append(b, indentBuf.Bytes()...) - b = encodeIndentComma(b) + b = encodeIndentComma(bb) code = code.next case opMarshalText: - ptr := load(ctxptr, code.idx) - isPtr := code.typ.Kind() == reflect.Ptr - p := ptrToUnsafePtr(ptr) - if p == nil { - b = encodeNull(b) - b = encodeIndentComma(b) - } else if isPtr && *(*unsafe.Pointer)(p) == nil { - b = append(b, '"', '"', ',', '\n') - } else { - if isPtr && code.typ.Elem().Implements(marshalTextType) { - p = *(*unsafe.Pointer)(p) - } - v := *(*interface{})(unsafe.Pointer(&interfaceHeader{ - typ: code.typ, - ptr: p, - })) - bytes, err := v.(encoding.TextMarshaler).MarshalText() - if err != nil { - return nil, errMarshaler(code, err) - } - b = encodeEscapedString(b, *(*string)(unsafe.Pointer(&bytes))) + p := load(ctxptr, code.idx) + if p == 0 { + b = append(b, `""`...) b = encodeIndentComma(b) + code = code.next + break } + bb, err := encodeMarshalTextIndent(code, b, ptrToInterface(code, p), true) + if err != nil { + return nil, err + } + b = encodeIndentComma(bb) code = code.next case opSlice: p := load(ctxptr, code.idx) diff --git a/encode_vm_indent.go b/encode_vm_indent.go index 39230e4..8d7653e 100644 --- a/encode_vm_indent.go +++ b/encode_vm_indent.go @@ -2,11 +2,9 @@ package json import ( "bytes" - "encoding" "fmt" "math" "reflect" - "runtime" "sort" "strings" "unsafe" @@ -142,65 +140,32 @@ func encodeRunIndent(ctx *encodeRuntimeContext, b []byte, codeSet *opcodeSet, op b = bb code = code.next case opMarshalJSON: - ptr := load(ctxptr, code.idx) - if ptr == 0 { + p := load(ctxptr, code.idx) + if p == 0 { b = encodeNull(b) b = encodeIndentComma(b) code = code.next break } - v := ptrToInterface(code, ptr) - bb, err := v.(Marshaler).MarshalJSON() + bb, err := encodeMarshalJSONIndent(ctx, code, b, ptrToInterface(code, p), code.indent, false) if err != nil { - return nil, errMarshaler(code, err) - } - runtime.KeepAlive(v) - if len(bb) == 0 { - return nil, errUnexpectedEndOfJSON( - fmt.Sprintf("error calling MarshalJSON for type %s", code.typ), - 0, - ) - } - var compactBuf bytes.Buffer - if err := compact(&compactBuf, bb, false); err != nil { return nil, err } - var indentBuf bytes.Buffer - if err := encodeWithIndent( - &indentBuf, - compactBuf.Bytes(), - string(ctx.prefix)+strings.Repeat(string(ctx.indentStr), ctx.baseIndent+code.indent), - string(ctx.indentStr), - ); err != nil { - return nil, err - } - b = append(b, indentBuf.Bytes()...) - b = encodeIndentComma(b) + b = encodeIndentComma(bb) code = code.next case opMarshalText: - ptr := load(ctxptr, code.idx) - isPtr := code.typ.Kind() == reflect.Ptr - p := ptrToUnsafePtr(ptr) - if p == nil { - b = encodeNull(b) - b = encodeIndentComma(b) - } else if isPtr && *(*unsafe.Pointer)(p) == nil { - b = append(b, '"', '"', ',', '\n') - } else { - if isPtr && code.typ.Elem().Implements(marshalTextType) { - p = *(*unsafe.Pointer)(p) - } - v := *(*interface{})(unsafe.Pointer(&interfaceHeader{ - typ: code.typ, - ptr: p, - })) - bytes, err := v.(encoding.TextMarshaler).MarshalText() - if err != nil { - return nil, errMarshaler(code, err) - } - b = encodeNoEscapedString(b, *(*string)(unsafe.Pointer(&bytes))) + p := load(ctxptr, code.idx) + if p == 0 { + b = append(b, `""`...) b = encodeIndentComma(b) + code = code.next + break } + bb, err := encodeMarshalTextIndent(code, b, ptrToInterface(code, p), false) + if err != nil { + return nil, err + } + b = encodeIndentComma(bb) code = code.next case opSlice: p := load(ctxptr, code.idx) diff --git a/error.go b/error.go index 71fd94f..2487b07 100644 --- a/error.go +++ b/error.go @@ -1,9 +1,7 @@ package json import ( - "fmt" - "reflect" - "strconv" + "github.com/goccy/go-json/internal/errors" ) // Before Go 1.2, an InvalidUTF8Error was returned by Marshal when @@ -12,142 +10,40 @@ import ( // replacing invalid bytes with the Unicode replacement rune U+FFFD. // // Deprecated: No longer used; kept for compatibility. -type InvalidUTF8Error struct { - S string // the whole string value that caused the error -} - -func (e *InvalidUTF8Error) Error() string { - return fmt.Sprintf("json: invalid UTF-8 in string: %s", strconv.Quote(e.S)) -} +type InvalidUTF8Error = errors.InvalidUTF8Error // An InvalidUnmarshalError describes an invalid argument passed to Unmarshal. // (The argument to Unmarshal must be a non-nil pointer.) -type InvalidUnmarshalError struct { - Type reflect.Type -} - -func (e *InvalidUnmarshalError) Error() string { - if e.Type == nil { - return "json: Unmarshal(nil)" - } - - if e.Type.Kind() != reflect.Ptr { - return fmt.Sprintf("json: Unmarshal(non-pointer %s)", e.Type) - } - return fmt.Sprintf("json: Unmarshal(nil %s)", e.Type) -} +type InvalidUnmarshalError = errors.InvalidUnmarshalError // A MarshalerError represents an error from calling a MarshalJSON or MarshalText method. -type MarshalerError struct { - Type reflect.Type - Err error - sourceFunc string -} - -func (e *MarshalerError) Error() string { - srcFunc := e.sourceFunc - if srcFunc == "" { - srcFunc = "MarshalJSON" - } - return fmt.Sprintf("json: error calling %s for type %s: %s", srcFunc, e.Type, e.Err.Error()) -} - -// Unwrap returns the underlying error. -func (e *MarshalerError) Unwrap() error { return e.Err } +type MarshalerError = errors.MarshalerError // A SyntaxError is a description of a JSON syntax error. -type SyntaxError struct { - msg string // description of error - Offset int64 // error occurred after reading Offset bytes -} - -func (e *SyntaxError) Error() string { return e.msg } +type SyntaxError = errors.SyntaxError // An UnmarshalFieldError describes a JSON object key that // led to an unexported (and therefore unwritable) struct field. // // Deprecated: No longer used; kept for compatibility. -type UnmarshalFieldError struct { - Key string - Type reflect.Type - Field reflect.StructField -} - -func (e *UnmarshalFieldError) Error() string { - return fmt.Sprintf("json: cannot unmarshal object key %s into unexported field %s of type %s", - strconv.Quote(e.Key), e.Field.Name, e.Type.String(), - ) -} +type UnmarshalFieldError = errors.UnmarshalFieldError // An UnmarshalTypeError describes a JSON value that was // not appropriate for a value of a specific Go type. -type UnmarshalTypeError struct { - Value string // description of JSON value - "bool", "array", "number -5" - Type reflect.Type // type of Go value it could not be assigned to - Offset int64 // error occurred after reading Offset bytes - Struct string // name of the struct type containing the field - Field string // the full path from root node to the field -} - -func (e *UnmarshalTypeError) Error() string { - if e.Struct != "" || e.Field != "" { - return fmt.Sprintf("json: cannot unmarshal %s into Go struct field %s.%s of type %s", - e.Value, e.Struct, e.Field, e.Type, - ) - } - return fmt.Sprintf("json: cannot unmarshal %s into Go value of type %s", e.Value, e.Type) -} +type UnmarshalTypeError = errors.UnmarshalTypeError // An UnsupportedTypeError is returned by Marshal when attempting // to encode an unsupported value type. -type UnsupportedTypeError struct { - Type reflect.Type -} +type UnsupportedTypeError = errors.UnsupportedTypeError -func (e *UnsupportedTypeError) Error() string { - return fmt.Sprintf("json: unsupported type: %s", e.Type) -} +type UnsupportedValueError = errors.UnsupportedValueError -type UnsupportedValueError struct { - Value reflect.Value - Str string -} - -func (e *UnsupportedValueError) Error() string { - return fmt.Sprintf("json: unsupported value: %s", e.Str) -} - -func errExceededMaxDepth(c byte, cursor int64) *SyntaxError { - return &SyntaxError{ - msg: fmt.Sprintf(`invalid character "%c" exceeded max depth`, c), - Offset: cursor, - } -} - -func errNotAtBeginningOfValue(cursor int64) *SyntaxError { - return &SyntaxError{msg: "not at beginning of value", Offset: cursor} -} - -func errUnexpectedEndOfJSON(msg string, cursor int64) *SyntaxError { - return &SyntaxError{ - msg: fmt.Sprintf("json: %s unexpected end of JSON input", msg), - Offset: cursor, - } -} - -func errExpected(msg string, cursor int64) *SyntaxError { - return &SyntaxError{msg: fmt.Sprintf("expected %s", msg), Offset: cursor} -} - -func errInvalidCharacter(c byte, context string, cursor int64) *SyntaxError { - if c == 0 { - return &SyntaxError{ - msg: fmt.Sprintf("json: invalid character as %s", context), - Offset: cursor, - } - } - return &SyntaxError{ - msg: fmt.Sprintf("json: invalid character %c as %s", c, context), - Offset: cursor, - } -} +var ( + errExceededMaxDepth = errors.ErrExceededMaxDepth + errNotAtBeginningOfValue = errors.ErrNotAtBeginningOfValue + errUnexpectedEndOfJSON = errors.ErrUnexpectedEndOfJSON + errExpected = errors.ErrExpected + errInvalidCharacter = errors.ErrInvalidCharacter + errSyntax = errors.ErrSyntax + errMarshaler = errors.ErrMarshaler +) diff --git a/export_test.go b/export_test.go index aa72fdb..b0e3532 100644 --- a/export_test.go +++ b/export_test.go @@ -1,18 +1,6 @@ package json -import "reflect" - -func NewSyntaxError(msg string, offset int64) *SyntaxError { - return &SyntaxError{ - msg: msg, - Offset: offset, - } -} - -func NewMarshalerError(typ reflect.Type, err error, msg string) *MarshalerError { - return &MarshalerError{ - Type: typ, - Err: err, - sourceFunc: msg, - } -} +var ( + NewSyntaxError = errSyntax + NewMarshalerError = errMarshaler +) diff --git a/internal/errors/error.go b/internal/errors/error.go new file mode 100644 index 0000000..329e2f1 --- /dev/null +++ b/internal/errors/error.go @@ -0,0 +1,157 @@ +package errors + +import ( + "fmt" + "reflect" + "strconv" +) + +type InvalidUTF8Error struct { + S string // the whole string value that caused the error +} + +func (e *InvalidUTF8Error) Error() string { + return fmt.Sprintf("json: invalid UTF-8 in string: %s", strconv.Quote(e.S)) +} + +type InvalidUnmarshalError struct { + Type reflect.Type +} + +func (e *InvalidUnmarshalError) Error() string { + if e.Type == nil { + return "json: Unmarshal(nil)" + } + + if e.Type.Kind() != reflect.Ptr { + return fmt.Sprintf("json: Unmarshal(non-pointer %s)", e.Type) + } + return fmt.Sprintf("json: Unmarshal(nil %s)", e.Type) +} + +// A MarshalerError represents an error from calling a MarshalJSON or MarshalText method. +type MarshalerError struct { + Type reflect.Type + Err error + sourceFunc string +} + +func (e *MarshalerError) Error() string { + srcFunc := e.sourceFunc + if srcFunc == "" { + srcFunc = "MarshalJSON" + } + return fmt.Sprintf("json: error calling %s for type %s: %s", srcFunc, e.Type, e.Err.Error()) +} + +// Unwrap returns the underlying error. +func (e *MarshalerError) Unwrap() error { return e.Err } + +// A SyntaxError is a description of a JSON syntax error. +type SyntaxError struct { + msg string // description of error + Offset int64 // error occurred after reading Offset bytes +} + +func (e *SyntaxError) Error() string { return e.msg } + +// An UnmarshalFieldError describes a JSON object key that +// led to an unexported (and therefore unwritable) struct field. +// +// Deprecated: No longer used; kept for compatibility. +type UnmarshalFieldError struct { + Key string + Type reflect.Type + Field reflect.StructField +} + +func (e *UnmarshalFieldError) Error() string { + return fmt.Sprintf("json: cannot unmarshal object key %s into unexported field %s of type %s", + strconv.Quote(e.Key), e.Field.Name, e.Type.String(), + ) +} + +// An UnmarshalTypeError describes a JSON value that was +// not appropriate for a value of a specific Go type. +type UnmarshalTypeError struct { + Value string // description of JSON value - "bool", "array", "number -5" + Type reflect.Type // type of Go value it could not be assigned to + Offset int64 // error occurred after reading Offset bytes + Struct string // name of the struct type containing the field + Field string // the full path from root node to the field +} + +func (e *UnmarshalTypeError) Error() string { + if e.Struct != "" || e.Field != "" { + return fmt.Sprintf("json: cannot unmarshal %s into Go struct field %s.%s of type %s", + e.Value, e.Struct, e.Field, e.Type, + ) + } + return fmt.Sprintf("json: cannot unmarshal %s into Go value of type %s", e.Value, e.Type) +} + +// An UnsupportedTypeError is returned by Marshal when attempting +// to encode an unsupported value type. +type UnsupportedTypeError struct { + Type reflect.Type +} + +func (e *UnsupportedTypeError) Error() string { + return fmt.Sprintf("json: unsupported type: %s", e.Type) +} + +type UnsupportedValueError struct { + Value reflect.Value + Str string +} + +func (e *UnsupportedValueError) Error() string { + return fmt.Sprintf("json: unsupported value: %s", e.Str) +} + +func ErrSyntax(msg string, offset int64) *SyntaxError { + return &SyntaxError{msg: msg, Offset: offset} +} + +func ErrMarshaler(typ reflect.Type, err error, msg string) *MarshalerError { + return &MarshalerError{ + Type: typ, + Err: err, + sourceFunc: msg, + } +} + +func ErrExceededMaxDepth(c byte, cursor int64) *SyntaxError { + return &SyntaxError{ + msg: fmt.Sprintf(`invalid character "%c" exceeded max depth`, c), + Offset: cursor, + } +} + +func ErrNotAtBeginningOfValue(cursor int64) *SyntaxError { + return &SyntaxError{msg: "not at beginning of value", Offset: cursor} +} + +func ErrUnexpectedEndOfJSON(msg string, cursor int64) *SyntaxError { + return &SyntaxError{ + msg: fmt.Sprintf("json: %s unexpected end of JSON input", msg), + Offset: cursor, + } +} + +func ErrExpected(msg string, cursor int64) *SyntaxError { + return &SyntaxError{msg: fmt.Sprintf("expected %s", msg), Offset: cursor} +} + +func ErrInvalidCharacter(c byte, context string, cursor int64) *SyntaxError { + if c == 0 { + return &SyntaxError{ + msg: fmt.Sprintf("json: invalid character as %s", context), + Offset: cursor, + } + } + return &SyntaxError{ + msg: fmt.Sprintf("json: invalid character %c as %s", c, context), + Offset: cursor, + } +}