mirror of https://github.com/goccy/go-json.git
commit
3ee1ab2711
|
@ -1,12 +1,20 @@
|
|||
name: Go
|
||||
on: [push, pull_request]
|
||||
jobs:
|
||||
build:
|
||||
name: Build on limited environment
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: build
|
||||
run: docker-compose run go-json
|
||||
test:
|
||||
name: Test
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ "ubuntu-latest", "macos-latest", "windows-latest" ]
|
||||
go-version: [ "1.13", "1.14", "1.15" ]
|
||||
go-version: [ "1.13", "1.14", "1.15", "1.16" ]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: setup Go ${{ matrix.go-version }}
|
||||
|
@ -30,7 +38,7 @@ jobs:
|
|||
- name: setup Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.15
|
||||
go-version: 1.16
|
||||
- name: checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: measure coverage
|
||||
|
|
|
@ -15,11 +15,8 @@ type opType struct {
|
|||
Op string
|
||||
Code string
|
||||
HeadToPtrHead func() string
|
||||
HeadToNPtrHead func() string
|
||||
HeadToAnonymousHead func() string
|
||||
HeadToOmitEmptyHead func() string
|
||||
HeadToStringTagHead func() string
|
||||
HeadToOnlyHead func() string
|
||||
PtrHeadToHead func() string
|
||||
FieldToEnd func() string
|
||||
FieldToOmitEmptyField func() string
|
||||
|
@ -30,14 +27,6 @@ func (t opType) IsHeadToPtrHead() bool {
|
|||
return t.Op != t.HeadToPtrHead()
|
||||
}
|
||||
|
||||
func (t opType) IsHeadToNPtrHead() bool {
|
||||
return t.Op != t.HeadToNPtrHead()
|
||||
}
|
||||
|
||||
func (t opType) IsHeadToAnonymousHead() bool {
|
||||
return t.Op != t.HeadToAnonymousHead()
|
||||
}
|
||||
|
||||
func (t opType) IsHeadToOmitEmptyHead() bool {
|
||||
return t.Op != t.HeadToOmitEmptyHead()
|
||||
}
|
||||
|
@ -50,10 +39,6 @@ func (t opType) IsPtrHeadToHead() bool {
|
|||
return t.Op != t.PtrHeadToHead()
|
||||
}
|
||||
|
||||
func (t opType) IsHeadToOnlyHead() bool {
|
||||
return t.Op != t.HeadToOnlyHead()
|
||||
}
|
||||
|
||||
func (t opType) IsFieldToEnd() bool {
|
||||
return t.Op != t.FieldToEnd()
|
||||
}
|
||||
|
@ -71,11 +56,8 @@ func createOpType(op, code string) opType {
|
|||
Op: op,
|
||||
Code: code,
|
||||
HeadToPtrHead: func() string { return op },
|
||||
HeadToNPtrHead: func() string { return op },
|
||||
HeadToAnonymousHead: func() string { return op },
|
||||
HeadToOmitEmptyHead: func() string { return op },
|
||||
HeadToStringTagHead: func() string { return op },
|
||||
HeadToOnlyHead: func() string { return op },
|
||||
PtrHeadToHead: func() string { return op },
|
||||
FieldToEnd: func() string { return op },
|
||||
FieldToOmitEmptyField: func() string { return op },
|
||||
|
@ -127,19 +109,19 @@ func (t opType) codeType() codeType {
|
|||
}
|
||||
return codeStructField
|
||||
}
|
||||
if strings.Contains(t.String(), "ArrayHead") {
|
||||
if t.String() == "Array" || t.String() == "ArrayPtr" {
|
||||
return codeArrayHead
|
||||
}
|
||||
if strings.Contains(t.String(), "ArrayElem") {
|
||||
return codeArrayElem
|
||||
}
|
||||
if strings.Contains(t.String(), "SliceHead") {
|
||||
if t.String() == "Slice" || t.String() == "SlicePtr" {
|
||||
return codeSliceHead
|
||||
}
|
||||
if strings.Contains(t.String(), "SliceElem") {
|
||||
return codeSliceElem
|
||||
}
|
||||
if strings.Contains(t.String(), "MapHead") {
|
||||
if t.String() == "Map" || t.String() == "MapPtr" {
|
||||
return codeMapHead
|
||||
}
|
||||
if strings.Contains(t.String(), "MapKey") {
|
||||
|
@ -159,9 +141,6 @@ func (t opType) headToPtrHead() opType {
|
|||
if strings.Index(t.String(), "PtrHead") > 0 {
|
||||
return t
|
||||
}
|
||||
if strings.Index(t.String(), "PtrAnonymousHead") > 0 {
|
||||
return t
|
||||
}
|
||||
|
||||
idx := strings.Index(t.String(), "Field")
|
||||
if idx == -1 {
|
||||
|
@ -169,44 +148,15 @@ func (t opType) headToPtrHead() opType {
|
|||
}
|
||||
suffix := "Ptr"+t.String()[idx+len("Field"):]
|
||||
|
||||
const toPtrOffset = 12
|
||||
const toPtrOffset = 3
|
||||
if strings.Contains(opType(int(t) + toPtrOffset).String(), suffix) {
|
||||
return opType(int(t) + toPtrOffset)
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func (t opType) headToNPtrHead() opType {
|
||||
if strings.Index(t.String(), "PtrHead") > 0 {
|
||||
return t
|
||||
}
|
||||
if strings.Index(t.String(), "PtrAnonymousHead") > 0 {
|
||||
return t
|
||||
}
|
||||
|
||||
idx := strings.Index(t.String(), "Field")
|
||||
if idx == -1 {
|
||||
return t
|
||||
}
|
||||
suffix := "NPtr"+t.String()[idx+len("Field"):]
|
||||
|
||||
const toPtrOffset = 24
|
||||
if strings.Contains(opType(int(t) + toPtrOffset).String(), suffix) {
|
||||
return opType(int(t) + toPtrOffset)
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func (t opType) headToAnonymousHead() opType {
|
||||
const toAnonymousOffset = 6
|
||||
if strings.Contains(opType(int(t) + toAnonymousOffset).String(), "Anonymous") {
|
||||
return opType(int(t) + toAnonymousOffset)
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func (t opType) headToOmitEmptyHead() opType {
|
||||
const toOmitEmptyOffset = 2
|
||||
const toOmitEmptyOffset = 1
|
||||
if strings.Contains(opType(int(t) + toOmitEmptyOffset).String(), "OmitEmpty") {
|
||||
return opType(int(t) + toOmitEmptyOffset)
|
||||
}
|
||||
|
@ -215,25 +165,13 @@ func (t opType) headToOmitEmptyHead() opType {
|
|||
}
|
||||
|
||||
func (t opType) headToStringTagHead() opType {
|
||||
const toStringTagOffset = 4
|
||||
const toStringTagOffset = 2
|
||||
if strings.Contains(opType(int(t) + toStringTagOffset).String(), "StringTag") {
|
||||
return opType(int(t) + toStringTagOffset)
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func (t opType) headToOnlyHead() opType {
|
||||
if strings.HasSuffix(t.String(), "Head") || strings.HasSuffix(t.String(), "HeadOmitEmpty") || strings.HasSuffix(t.String(), "HeadStringTag") {
|
||||
return t
|
||||
}
|
||||
|
||||
const toOnlyOffset = 1
|
||||
if opType(int(t) + toOnlyOffset).String() == t.String() + "Only" {
|
||||
return opType(int(t) + toOnlyOffset)
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func (t opType) ptrHeadToHead() opType {
|
||||
idx := strings.Index(t.String(), "Ptr")
|
||||
if idx == -1 {
|
||||
|
@ -241,7 +179,7 @@ func (t opType) ptrHeadToHead() opType {
|
|||
}
|
||||
suffix := t.String()[idx+len("Ptr"):]
|
||||
|
||||
const toPtrOffset = 12
|
||||
const toPtrOffset = 3
|
||||
if strings.Contains(opType(int(t) - toPtrOffset).String(), suffix) {
|
||||
return opType(int(t) - toPtrOffset)
|
||||
}
|
||||
|
@ -295,11 +233,11 @@ func (t opType) fieldToStringTagField() opType {
|
|||
"StructEnd",
|
||||
}
|
||||
primitiveTypes := []string{
|
||||
"int", "uint", "float32", "float64", "bool", "string", "bytes",
|
||||
"array", "map", "mapLoad", "slice", "struct", "MarshalJSON", "MarshalText", "recursive",
|
||||
"int", "uint", "float32", "float64", "bool", "string", "bytes", "number",
|
||||
"array", "map", "slice", "struct", "MarshalJSON", "MarshalText", "recursive",
|
||||
"intString", "uintString",
|
||||
"intPtr", "uintPtr", "float32Ptr", "float64Ptr", "boolPtr", "stringPtr", "bytesPtr",
|
||||
"intNPtr", "uintNPtr", "float32NPtr", "float64NPtr", "boolNPtr", "stringNPtr", "bytesNPtr",
|
||||
"intPtr", "uintPtr", "float32Ptr", "float64Ptr", "boolPtr", "stringPtr", "bytesPtr", "numberPtr",
|
||||
"arrayPtr", "mapPtr", "slicePtr", "marshalJSONPtr", "marshalTextPtr", "interfacePtr", "recursivePtr",
|
||||
}
|
||||
primitiveTypesUpper := []string{}
|
||||
for _, typ := range primitiveTypes {
|
||||
|
@ -309,17 +247,10 @@ func (t opType) fieldToStringTagField() opType {
|
|||
createOpType("End", "Op"),
|
||||
createOpType("Interface", "Op"),
|
||||
createOpType("Ptr", "Op"),
|
||||
createOpType("NPtr", "Op"),
|
||||
createOpType("SliceHead", "SliceHead"),
|
||||
createOpType("RootSliceHead", "SliceHead"),
|
||||
createOpType("SliceElem", "SliceElem"),
|
||||
createOpType("RootSliceElem", "SliceElem"),
|
||||
createOpType("SliceEnd", "Op"),
|
||||
createOpType("ArrayHead", "ArrayHead"),
|
||||
createOpType("ArrayElem", "ArrayElem"),
|
||||
createOpType("ArrayEnd", "Op"),
|
||||
createOpType("MapHead", "MapHead"),
|
||||
createOpType("MapHeadLoad", "MapHead"),
|
||||
createOpType("MapKey", "MapKey"),
|
||||
createOpType("MapValue", "MapValue"),
|
||||
createOpType("MapEnd", "Op"),
|
||||
|
@ -331,94 +262,47 @@ func (t opType) fieldToStringTagField() opType {
|
|||
opTypes = append(opTypes, createOpType(typ, "Op"))
|
||||
}
|
||||
for _, typ := range append(primitiveTypesUpper, "") {
|
||||
for _, ptrOrNot := range []string{"", "Ptr", "NPtr"} {
|
||||
for _, headType := range []string{"", "Anonymous"} {
|
||||
for _, ptrOrNot := range []string{"", "Ptr"} {
|
||||
for _, opt := range []string{"", "OmitEmpty", "StringTag"} {
|
||||
for _, onlyOrNot := range []string{"", "Only"} {
|
||||
ptrOrNot := ptrOrNot
|
||||
headType := headType
|
||||
opt := opt
|
||||
typ := typ
|
||||
onlyOrNot := onlyOrNot
|
||||
|
||||
op := fmt.Sprintf(
|
||||
"StructField%s%sHead%s%s%s",
|
||||
"StructField%sHead%s%s",
|
||||
ptrOrNot,
|
||||
headType,
|
||||
opt,
|
||||
typ,
|
||||
onlyOrNot,
|
||||
)
|
||||
opTypes = append(opTypes, opType{
|
||||
Op: op,
|
||||
Code: "StructField",
|
||||
HeadToPtrHead: func() string {
|
||||
return fmt.Sprintf(
|
||||
"StructFieldPtr%sHead%s%s%s",
|
||||
headType,
|
||||
"StructFieldPtrHead%s%s",
|
||||
opt,
|
||||
typ,
|
||||
onlyOrNot,
|
||||
)
|
||||
},
|
||||
HeadToNPtrHead: func() string {
|
||||
return fmt.Sprintf(
|
||||
"StructFieldNPtr%sHead%s%s%s",
|
||||
headType,
|
||||
opt,
|
||||
typ,
|
||||
onlyOrNot,
|
||||
)
|
||||
},
|
||||
HeadToAnonymousHead: func() string {
|
||||
return fmt.Sprintf(
|
||||
"StructField%sAnonymousHead%s%s%s",
|
||||
ptrOrNot,
|
||||
opt,
|
||||
typ,
|
||||
onlyOrNot,
|
||||
)
|
||||
},
|
||||
HeadToOmitEmptyHead: func() string {
|
||||
return fmt.Sprintf(
|
||||
"StructField%s%sHeadOmitEmpty%s%s",
|
||||
"StructField%sHeadOmitEmpty%s",
|
||||
ptrOrNot,
|
||||
headType,
|
||||
typ,
|
||||
onlyOrNot,
|
||||
)
|
||||
},
|
||||
HeadToStringTagHead: func() string {
|
||||
return fmt.Sprintf(
|
||||
"StructField%s%sHeadStringTag%s%s",
|
||||
"StructField%sHeadStringTag%s",
|
||||
ptrOrNot,
|
||||
headType,
|
||||
typ,
|
||||
onlyOrNot,
|
||||
)
|
||||
},
|
||||
HeadToOnlyHead: func() string {
|
||||
switch typ {
|
||||
case "", "Array", "Map", "MapLoad", "Slice",
|
||||
"Struct", "Recursive", "MarshalJSON", "MarshalText",
|
||||
"IntString", "UintString":
|
||||
return op
|
||||
}
|
||||
return fmt.Sprintf(
|
||||
"StructField%s%sHead%s%sOnly",
|
||||
ptrOrNot,
|
||||
headType,
|
||||
opt,
|
||||
typ,
|
||||
)
|
||||
},
|
||||
PtrHeadToHead: func() string {
|
||||
return fmt.Sprintf(
|
||||
"StructField%sHead%s%s%s",
|
||||
headType,
|
||||
"StructFieldHead%s%s",
|
||||
opt,
|
||||
typ,
|
||||
onlyOrNot,
|
||||
)
|
||||
},
|
||||
FieldToEnd: func() string { return op },
|
||||
|
@ -428,8 +312,6 @@ func (t opType) fieldToStringTagField() opType {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, typ := range append(primitiveTypesUpper, "") {
|
||||
for _, opt := range []string{"", "OmitEmpty", "StringTag"} {
|
||||
opt := opt
|
||||
|
@ -444,15 +326,12 @@ func (t opType) fieldToStringTagField() opType {
|
|||
Op: op,
|
||||
Code: "StructField",
|
||||
HeadToPtrHead: func() string { return op },
|
||||
HeadToNPtrHead: func() string { return op },
|
||||
HeadToAnonymousHead: func() string { return op },
|
||||
HeadToOmitEmptyHead: func() string { return op },
|
||||
HeadToStringTagHead: func() string { return op },
|
||||
HeadToOnlyHead: func() string { return op },
|
||||
PtrHeadToHead: func() string { return op },
|
||||
FieldToEnd: func() string {
|
||||
switch typ {
|
||||
case "", "Array", "Map", "MapLoad", "Slice", "Struct", "Recursive":
|
||||
case "", "Array", "Map", "Slice", "Struct", "Recursive":
|
||||
return op
|
||||
}
|
||||
return fmt.Sprintf(
|
||||
|
@ -490,11 +369,8 @@ func (t opType) fieldToStringTagField() opType {
|
|||
Op: op,
|
||||
Code: "StructEnd",
|
||||
HeadToPtrHead: func() string { return op },
|
||||
HeadToNPtrHead: func() string { return op },
|
||||
HeadToAnonymousHead: func() string { return op },
|
||||
HeadToOmitEmptyHead: func() string { return op },
|
||||
HeadToStringTagHead: func() string { return op },
|
||||
HeadToOnlyHead: func() string { return op },
|
||||
PtrHeadToHead: func() string { return op },
|
||||
FieldToEnd: func() string { return op },
|
||||
FieldToOmitEmptyField: func() string { return op },
|
||||
|
|
|
@ -5,6 +5,9 @@ import (
|
|||
)
|
||||
|
||||
func compact(dst *bytes.Buffer, src []byte, escape bool) error {
|
||||
if len(src) == 0 {
|
||||
return errUnexpectedEndOfJSON("", 0)
|
||||
}
|
||||
length := len(src)
|
||||
for cursor := 0; cursor < length; cursor++ {
|
||||
c := src[cursor]
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1774,11 +1774,11 @@ func TestCoverBool(t *testing.T) {
|
|||
enc.SetIndent("", " ")
|
||||
}
|
||||
if err := enc.Encode(test.data); err != nil {
|
||||
t.Fatalf("%s(htmlEscape:%T): %+v: %s", test.name, htmlEscape, test.data, err)
|
||||
t.Fatalf("%s(htmlEscape:%v,indent:%v): %+v: %s", test.name, htmlEscape, indent, test.data, err)
|
||||
}
|
||||
stdresult := encodeByEncodingJSON(test.data, indent, htmlEscape)
|
||||
if buf.String() != stdresult {
|
||||
t.Errorf("%s(htmlEscape:%T): doesn't compatible with encoding/json. expected %q but got %q", test.name, htmlEscape, stdresult, buf.String())
|
||||
t.Errorf("%s(htmlEscape:%v,indent:%v): doesn't compatible with encoding/json. expected %q but got %q", test.name, htmlEscape, indent, stdresult, buf.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,9 @@ func float32ptr(v float32) *float32 { return &v }
|
|||
func float64ptr(v float64) *float64 { return &v }
|
||||
func stringptr(v string) *string { return &v }
|
||||
func boolptr(v bool) *bool { return &v }
|
||||
func sliceptr(v []int) *[]int { return &v }
|
||||
func arrayptr(v [2]int) *[2]int { return &v }
|
||||
func mapptr(v map[string]int) *map[string]int { return &v }
|
||||
|
||||
func encodeByEncodingJSON(data interface{}, indent, escape bool) string {
|
||||
var buf bytes.Buffer
|
||||
|
|
|
@ -408,6 +408,29 @@ func TestCoverInt16(t *testing.T) {
|
|||
}{A: nil, B: nil},
|
||||
},
|
||||
|
||||
// PtrHeadInt16NilMultiFields
|
||||
{
|
||||
name: "PtrHeadInt16NilMultiFields",
|
||||
data: (*struct {
|
||||
A int16 `json:"a"`
|
||||
B int16 `json:"b"`
|
||||
})(nil),
|
||||
},
|
||||
{
|
||||
name: "PtrHeadInt16NilMultiFieldsOmitEmpty",
|
||||
data: (*struct {
|
||||
A int16 `json:"a,omitempty"`
|
||||
B int16 `json:"b,omitempty"`
|
||||
})(nil),
|
||||
},
|
||||
{
|
||||
name: "PtrHeadInt16NilMultiFieldsString",
|
||||
data: (*struct {
|
||||
A int16 `json:"a,string"`
|
||||
B int16 `json:"b,string"`
|
||||
})(nil),
|
||||
},
|
||||
|
||||
// PtrHeadInt16NilMultiFields
|
||||
{
|
||||
name: "PtrHeadInt16NilMultiFields",
|
||||
|
|
|
@ -1774,11 +1774,11 @@ func TestCoverInt(t *testing.T) {
|
|||
enc.SetIndent("", " ")
|
||||
}
|
||||
if err := enc.Encode(test.data); err != nil {
|
||||
t.Fatalf("%s(htmlEscape:%T): %+v: %s", test.name, htmlEscape, test.data, err)
|
||||
t.Fatalf("%s(htmlEscape:%v,indent:%v): %+v: %s", test.name, htmlEscape, indent, test.data, err)
|
||||
}
|
||||
stdresult := encodeByEncodingJSON(test.data, indent, htmlEscape)
|
||||
if buf.String() != stdresult {
|
||||
t.Errorf("%s(htmlEscape:%T): doesn't compatible with encoding/json. expected %q but got %q", test.name, htmlEscape, stdresult, buf.String())
|
||||
t.Errorf("%s(htmlEscape:%v,indent:%v): doesn't compatible with encoding/json. expected %q but got %q", test.name, htmlEscape, indent, stdresult, buf.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1793,11 +1793,11 @@ func TestCoverString(t *testing.T) {
|
|||
enc.SetIndent("", " ")
|
||||
}
|
||||
if err := enc.Encode(test.data); err != nil {
|
||||
t.Fatalf("%s(htmlEscape:%T): %v: %s", test.name, htmlEscape, test.data, err)
|
||||
t.Fatalf("%s(htmlEscape:%v,indent:%v): %v: %s", test.name, htmlEscape, indent, test.data, err)
|
||||
}
|
||||
stdresult := encodeByEncodingJSON(test.data, indent, htmlEscape)
|
||||
if buf.String() != stdresult {
|
||||
t.Errorf("%s(htmlEscape:%T): doesn't compatible with encoding/json. expected %q but got %q", test.name, htmlEscape, stdresult, buf.String())
|
||||
t.Errorf("%s(htmlEscape:%v,indent:%v): doesn't compatible with encoding/json. expected %q but got %q", test.name, htmlEscape, indent, stdresult, buf.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1774,11 +1774,11 @@ func TestCoverUint16(t *testing.T) {
|
|||
enc.SetIndent("", " ")
|
||||
}
|
||||
if err := enc.Encode(test.data); err != nil {
|
||||
t.Fatalf("%s(htmlEscape:%T): %+v: %s", test.name, htmlEscape, test.data, err)
|
||||
t.Fatalf("%s(htmlEscape:%v,indent:%v): %+v: %s", test.name, htmlEscape, indent, test.data, err)
|
||||
}
|
||||
stdresult := encodeByEncodingJSON(test.data, indent, htmlEscape)
|
||||
if buf.String() != stdresult {
|
||||
t.Errorf("%s(htmlEscape:%T): doesn't compatible with encoding/json. expected %q but got %q", test.name, htmlEscape, stdresult, buf.String())
|
||||
t.Errorf("%s(htmlEscape:%v,indent:%v): doesn't compatible with encoding/json. expected %q but got %q", test.name, htmlEscape, indent, stdresult, buf.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -80,7 +80,7 @@ func decodeCompile(typ *rtype, structName, fieldName string, structTypeToDecoder
|
|||
case reflect.Uint64:
|
||||
return decodeCompileUint64(typ, structName, fieldName)
|
||||
case reflect.String:
|
||||
return decodeCompileString(structName, fieldName)
|
||||
return decodeCompileString(typ, structName, fieldName)
|
||||
case reflect.Bool:
|
||||
return decodeCompileBool(structName, fieldName)
|
||||
case reflect.Float32:
|
||||
|
@ -203,7 +203,12 @@ func decodeCompileFloat64(structName, fieldName string) (decoder, error) {
|
|||
}), nil
|
||||
}
|
||||
|
||||
func decodeCompileString(structName, fieldName string) (decoder, error) {
|
||||
func decodeCompileString(typ *rtype, structName, fieldName string) (decoder, error) {
|
||||
if typ == type2rtype(jsonNumberType) {
|
||||
return newNumberDecoder(structName, fieldName, func(p unsafe.Pointer, v Number) {
|
||||
*(*Number)(p) = v
|
||||
}), nil
|
||||
}
|
||||
return newStringDecoder(structName, fieldName), nil
|
||||
}
|
||||
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
package json
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type numberDecoder struct {
|
||||
*floatDecoder
|
||||
stringDecoder *stringDecoder
|
||||
op func(unsafe.Pointer, Number)
|
||||
structName string
|
||||
fieldName string
|
||||
|
@ -13,7 +14,7 @@ type numberDecoder struct {
|
|||
|
||||
func newNumberDecoder(structName, fieldName string, op func(unsafe.Pointer, Number)) *numberDecoder {
|
||||
return &numberDecoder{
|
||||
floatDecoder: newFloatDecoder(structName, fieldName, nil),
|
||||
stringDecoder: newStringDecoder(structName, fieldName),
|
||||
op: op,
|
||||
structName: structName,
|
||||
fieldName: fieldName,
|
||||
|
@ -21,22 +22,97 @@ func newNumberDecoder(structName, fieldName string, op func(unsafe.Pointer, Numb
|
|||
}
|
||||
|
||||
func (d *numberDecoder) decodeStream(s *stream, depth int64, p unsafe.Pointer) error {
|
||||
bytes, err := d.floatDecoder.decodeStreamByte(s)
|
||||
bytes, err := d.decodeStreamByte(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := strconv.ParseFloat(*(*string)(unsafe.Pointer(&bytes)), 64); err != nil {
|
||||
return &SyntaxError{msg: err.Error(), Offset: s.totalOffset()}
|
||||
}
|
||||
d.op(p, Number(string(bytes)))
|
||||
s.reset()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *numberDecoder) decode(buf []byte, cursor, depth int64, p unsafe.Pointer) (int64, error) {
|
||||
bytes, c, err := d.floatDecoder.decodeByte(buf, cursor)
|
||||
bytes, c, err := d.decodeByte(buf, cursor)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if _, err := strconv.ParseFloat(*(*string)(unsafe.Pointer(&bytes)), 64); err != nil {
|
||||
return 0, &SyntaxError{msg: err.Error(), Offset: c}
|
||||
}
|
||||
cursor = c
|
||||
s := *(*string)(unsafe.Pointer(&bytes))
|
||||
d.op(p, Number(s))
|
||||
return cursor, nil
|
||||
}
|
||||
|
||||
func (d *numberDecoder) decodeStreamByte(s *stream) ([]byte, error) {
|
||||
for {
|
||||
switch s.char() {
|
||||
case ' ', '\n', '\t', '\r':
|
||||
s.cursor++
|
||||
continue
|
||||
case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
return floatBytes(s), nil
|
||||
case 'n':
|
||||
if err := nullBytes(s); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, nil
|
||||
case '"':
|
||||
return d.stringDecoder.decodeStreamByte(s)
|
||||
case nul:
|
||||
if s.read() {
|
||||
continue
|
||||
}
|
||||
goto ERROR
|
||||
default:
|
||||
goto ERROR
|
||||
}
|
||||
}
|
||||
ERROR:
|
||||
return nil, errUnexpectedEndOfJSON("json.Number", s.totalOffset())
|
||||
}
|
||||
|
||||
func (d *numberDecoder) decodeByte(buf []byte, cursor int64) ([]byte, int64, error) {
|
||||
buflen := int64(len(buf))
|
||||
for ; cursor < buflen; cursor++ {
|
||||
switch buf[cursor] {
|
||||
case ' ', '\n', '\t', '\r':
|
||||
continue
|
||||
case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
start := cursor
|
||||
cursor++
|
||||
for ; cursor < buflen; cursor++ {
|
||||
if floatTable[buf[cursor]] {
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
num := buf[start:cursor]
|
||||
return num, cursor, nil
|
||||
case 'n':
|
||||
if cursor+3 >= buflen {
|
||||
return nil, 0, errUnexpectedEndOfJSON("null", cursor)
|
||||
}
|
||||
if buf[cursor+1] != 'u' {
|
||||
return nil, 0, errInvalidCharacter(buf[cursor+1], "null", cursor)
|
||||
}
|
||||
if buf[cursor+2] != 'l' {
|
||||
return nil, 0, errInvalidCharacter(buf[cursor+2], "null", cursor)
|
||||
}
|
||||
if buf[cursor+3] != 'l' {
|
||||
return nil, 0, errInvalidCharacter(buf[cursor+3], "null", cursor)
|
||||
}
|
||||
cursor += 4
|
||||
return nil, cursor, nil
|
||||
case '"':
|
||||
return d.stringDecoder.decodeByte(buf, cursor)
|
||||
default:
|
||||
return nil, 0, errUnexpectedEndOfJSON("json.Number", cursor)
|
||||
}
|
||||
}
|
||||
return nil, 0, errUnexpectedEndOfJSON("json.Number", cursor)
|
||||
}
|
||||
|
|
|
@ -1251,31 +1251,31 @@ var unmarshalTests = []unmarshalTest{
|
|||
in: `invalid`, // 143
|
||||
ptr: new(json.Number),
|
||||
err: json.NewSyntaxError(
|
||||
`json: invalid character v as null`,
|
||||
`json: json.Number unexpected end of JSON input`,
|
||||
1,
|
||||
),
|
||||
},
|
||||
{
|
||||
in: `"invalid"`, // 144
|
||||
ptr: new(json.Number),
|
||||
err: fmt.Errorf(`strconv.ParseFloat: parsing "\"invalid\"": invalid syntax`),
|
||||
err: fmt.Errorf(`strconv.ParseFloat: parsing "invalid": invalid syntax`),
|
||||
},
|
||||
{
|
||||
in: `{"A":"invalid"}`, // 145
|
||||
ptr: new(struct{ A json.Number }),
|
||||
err: fmt.Errorf(`strconv.ParseFloat: parsing "\"invalid\"": invalid syntax`),
|
||||
err: fmt.Errorf(`strconv.ParseFloat: parsing "invalid": invalid syntax`),
|
||||
},
|
||||
{
|
||||
in: `{"A":"invalid"}`, // 146
|
||||
ptr: new(struct {
|
||||
A json.Number `json:",string"`
|
||||
}),
|
||||
err: fmt.Errorf(`json: null unexpected end of JSON input`),
|
||||
err: fmt.Errorf(`json: json.Number unexpected end of JSON input`),
|
||||
},
|
||||
{
|
||||
in: `{"A":"invalid"}`, // 147
|
||||
ptr: new(map[string]json.Number),
|
||||
err: fmt.Errorf(`strconv.ParseFloat: parsing "\"invalid\"": invalid syntax`),
|
||||
err: fmt.Errorf(`strconv.ParseFloat: parsing "invalid": invalid syntax`),
|
||||
},
|
||||
/*
|
||||
// invalid UTF-8 is coerced to valid UTF-8.
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
version: '2'
|
||||
services:
|
||||
go-json:
|
||||
image: golang:1.16
|
||||
volumes:
|
||||
- '.:/go/src/go-json'
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 2048M
|
||||
working_dir: /go/src/go-json
|
||||
command: |
|
||||
sh -c "go test -c . && ls go-json.test"
|
144
encode.go
144
encode.go
|
@ -2,10 +2,14 @@ package json
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"unsafe"
|
||||
)
|
||||
|
@ -358,7 +362,147 @@ func encodeByteSlice(b []byte, src []byte) []byte {
|
|||
return append(append(b, buf...), '"')
|
||||
}
|
||||
|
||||
func encodeNumber(b []byte, n Number) ([]byte, error) {
|
||||
if len(n) == 0 {
|
||||
return append(b, '0'), nil
|
||||
}
|
||||
for i := 0; i < len(n); i++ {
|
||||
if !floatTable[n[i]] {
|
||||
return nil, fmt.Errorf("json: invalid number literal %q", n)
|
||||
}
|
||||
}
|
||||
b = append(b, n...)
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func appendIndent(ctx *encodeRuntimeContext, b []byte, indent int) []byte {
|
||||
b = append(b, ctx.prefix...)
|
||||
return append(b, bytes.Repeat(ctx.indentStr, ctx.baseIndent+indent)...)
|
||||
}
|
||||
|
||||
func encodeIsNilForMarshaler(v interface{}) bool {
|
||||
rv := reflect.ValueOf(v)
|
||||
switch rv.Kind() {
|
||||
case reflect.Interface, reflect.Map, reflect.Ptr:
|
||||
return rv.IsNil()
|
||||
case reflect.Slice:
|
||||
return rv.IsNil() || rv.Len() == 0
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func encodeMarshalJSON(code *opcode, b []byte, v interface{}, escape bool) ([]byte, error) {
|
||||
rv := reflect.ValueOf(v) // convert by dynamic interface type
|
||||
if code.addrForMarshaler {
|
||||
if rv.CanAddr() {
|
||||
rv = rv.Addr()
|
||||
} else {
|
||||
newV := reflect.New(rv.Type())
|
||||
newV.Elem().Set(rv)
|
||||
rv = newV
|
||||
}
|
||||
}
|
||||
v = rv.Interface()
|
||||
marshaler, ok := v.(Marshaler)
|
||||
if !ok {
|
||||
return encodeNull(b), nil
|
||||
}
|
||||
bb, err := marshaler.MarshalJSON()
|
||||
if err != nil {
|
||||
return nil, &MarshalerError{Type: reflect.TypeOf(v), Err: err}
|
||||
}
|
||||
buf := bytes.NewBuffer(b)
|
||||
//TODO: we should validate buffer with `compact`
|
||||
if err := compact(buf, bb, escape); err != nil {
|
||||
return nil, &MarshalerError{Type: reflect.TypeOf(v), Err: err}
|
||||
}
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func encodeMarshalJSONIndent(ctx *encodeRuntimeContext, code *opcode, b []byte, v interface{}, indent int, escape bool) ([]byte, error) {
|
||||
rv := reflect.ValueOf(v) // convert by dynamic interface type
|
||||
if code.addrForMarshaler {
|
||||
if rv.CanAddr() {
|
||||
rv = rv.Addr()
|
||||
} else {
|
||||
newV := reflect.New(rv.Type())
|
||||
newV.Elem().Set(rv)
|
||||
rv = newV
|
||||
}
|
||||
}
|
||||
v = rv.Interface()
|
||||
marshaler, ok := v.(Marshaler)
|
||||
if !ok {
|
||||
return encodeNull(b), nil
|
||||
}
|
||||
bb, err := marshaler.MarshalJSON()
|
||||
if err != nil {
|
||||
return nil, &MarshalerError{Type: reflect.TypeOf(v), Err: err}
|
||||
}
|
||||
var compactBuf bytes.Buffer
|
||||
if err := compact(&compactBuf, bb, escape); err != nil {
|
||||
return nil, &MarshalerError{Type: reflect.TypeOf(v), Err: err}
|
||||
}
|
||||
var indentBuf bytes.Buffer
|
||||
if err := encodeWithIndent(
|
||||
&indentBuf,
|
||||
compactBuf.Bytes(),
|
||||
string(ctx.prefix)+strings.Repeat(string(ctx.indentStr), ctx.baseIndent+indent+1),
|
||||
string(ctx.indentStr),
|
||||
); err != nil {
|
||||
return nil, &MarshalerError{Type: reflect.TypeOf(v), Err: err}
|
||||
}
|
||||
return append(b, indentBuf.Bytes()...), nil
|
||||
}
|
||||
|
||||
func encodeMarshalText(code *opcode, b []byte, v interface{}, escape bool) ([]byte, error) {
|
||||
rv := reflect.ValueOf(v) // convert by dynamic interface type
|
||||
if code.addrForMarshaler {
|
||||
if rv.CanAddr() {
|
||||
rv = rv.Addr()
|
||||
} else {
|
||||
newV := reflect.New(rv.Type())
|
||||
newV.Elem().Set(rv)
|
||||
rv = newV
|
||||
}
|
||||
}
|
||||
v = rv.Interface()
|
||||
marshaler, ok := v.(encoding.TextMarshaler)
|
||||
if !ok {
|
||||
return encodeNull(b), nil
|
||||
}
|
||||
bytes, err := marshaler.MarshalText()
|
||||
if err != nil {
|
||||
return nil, &MarshalerError{Type: reflect.TypeOf(v), Err: err}
|
||||
}
|
||||
if escape {
|
||||
return encodeEscapedString(b, *(*string)(unsafe.Pointer(&bytes))), nil
|
||||
}
|
||||
return encodeNoEscapedString(b, *(*string)(unsafe.Pointer(&bytes))), nil
|
||||
}
|
||||
|
||||
func encodeMarshalTextIndent(code *opcode, b []byte, v interface{}, escape bool) ([]byte, error) {
|
||||
rv := reflect.ValueOf(v) // convert by dynamic interface type
|
||||
if code.addrForMarshaler {
|
||||
if rv.CanAddr() {
|
||||
rv = rv.Addr()
|
||||
} else {
|
||||
newV := reflect.New(rv.Type())
|
||||
newV.Elem().Set(rv)
|
||||
rv = newV
|
||||
}
|
||||
}
|
||||
v = rv.Interface()
|
||||
marshaler, ok := v.(encoding.TextMarshaler)
|
||||
if !ok {
|
||||
return encodeNull(b), nil
|
||||
}
|
||||
bytes, err := marshaler.MarshalText()
|
||||
if err != nil {
|
||||
return nil, &MarshalerError{Type: reflect.TypeOf(v), Err: err}
|
||||
}
|
||||
if escape {
|
||||
return encodeEscapedString(b, *(*string)(unsafe.Pointer(&bytes))), nil
|
||||
}
|
||||
return encodeNoEscapedString(b, *(*string)(unsafe.Pointer(&bytes))), nil
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ type opcodeSet struct {
|
|||
var (
|
||||
marshalJSONType = reflect.TypeOf((*Marshaler)(nil)).Elem()
|
||||
marshalTextType = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem()
|
||||
jsonNumberType = reflect.TypeOf(Number(""))
|
||||
)
|
||||
|
||||
func encodeCompileToGetCodeSetSlowPath(typeptr uintptr) (*opcodeSet, error) {
|
||||
|
@ -37,7 +38,6 @@ func encodeCompileToGetCodeSetSlowPath(typeptr uintptr) (*opcodeSet, error) {
|
|||
|
||||
code, err := encodeCompileHead(&encodeCompileContext{
|
||||
typ: copiedType,
|
||||
root: true,
|
||||
structTypeToCompiledCode: map[uintptr]*compiledCode{},
|
||||
})
|
||||
if err != nil {
|
||||
|
@ -56,42 +56,44 @@ func encodeCompileToGetCodeSetSlowPath(typeptr uintptr) (*opcodeSet, error) {
|
|||
func encodeCompileHead(ctx *encodeCompileContext) (*opcode, error) {
|
||||
typ := ctx.typ
|
||||
switch {
|
||||
case typ.Implements(marshalJSONType):
|
||||
case encodeImplementsMarshalJSON(typ):
|
||||
return encodeCompileMarshalJSON(ctx)
|
||||
case rtype_ptrTo(typ).Implements(marshalJSONType):
|
||||
return encodeCompileMarshalJSONPtr(ctx)
|
||||
case typ.Implements(marshalTextType):
|
||||
case encodeImplementsMarshalText(typ):
|
||||
return encodeCompileMarshalText(ctx)
|
||||
case rtype_ptrTo(typ).Implements(marshalTextType):
|
||||
return encodeCompileMarshalTextPtr(ctx)
|
||||
}
|
||||
|
||||
isPtr := false
|
||||
orgType := typ
|
||||
if typ.Kind() == reflect.Ptr {
|
||||
typ = typ.Elem()
|
||||
isPtr = true
|
||||
}
|
||||
switch {
|
||||
case encodeImplementsMarshalJSON(typ):
|
||||
return encodeCompileMarshalJSON(ctx)
|
||||
case encodeImplementsMarshalText(typ):
|
||||
return encodeCompileMarshalText(ctx)
|
||||
}
|
||||
if typ.Kind() == reflect.Map {
|
||||
return encodeCompileMap(ctx.withType(typ), isPtr)
|
||||
if isPtr {
|
||||
return encodeCompilePtr(ctx.withType(rtype_ptrTo(typ)))
|
||||
}
|
||||
return encodeCompileMap(ctx.withType(typ))
|
||||
} else if typ.Kind() == reflect.Struct {
|
||||
code, err := encodeCompileStruct(ctx.withType(typ), isPtr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
encodeConvertHeadOnlyCode(code, isPtr)
|
||||
encodeOptimizeStructEnd(code)
|
||||
encodeLinkRecursiveCode(code)
|
||||
return code, nil
|
||||
} else if isPtr && typ.Implements(marshalTextType) {
|
||||
typ = orgType
|
||||
} else if isPtr && typ.Implements(marshalJSONType) {
|
||||
typ = orgType
|
||||
}
|
||||
code, err := encodeCompile(ctx.withType(typ))
|
||||
code, err := encodeCompile(ctx.withType(typ), isPtr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
encodeConvertHeadOnlyCode(code, isPtr)
|
||||
encodeOptimizeStructEnd(code)
|
||||
encodeLinkRecursiveCode(code)
|
||||
return code, nil
|
||||
|
@ -100,9 +102,7 @@ func encodeCompileHead(ctx *encodeCompileContext) (*opcode, error) {
|
|||
func encodeLinkRecursiveCode(c *opcode) {
|
||||
for code := c; code.op != opEnd && code.op != opStructFieldRecursiveEnd; {
|
||||
switch code.op {
|
||||
case opStructFieldRecursive,
|
||||
opStructFieldPtrAnonymousHeadRecursive,
|
||||
opStructFieldAnonymousHeadRecursive:
|
||||
case opStructFieldRecursive, opStructFieldRecursivePtr:
|
||||
if code.jmp.linked {
|
||||
code = code.next
|
||||
continue
|
||||
|
@ -143,7 +143,7 @@ func encodeLinkRecursiveCode(c *opcode) {
|
|||
|
||||
func encodeOptimizeStructEnd(c *opcode) {
|
||||
for code := c; code.op != opEnd; {
|
||||
if code.op == opStructFieldRecursive {
|
||||
if code.op == opStructFieldRecursive || code.op == opStructFieldRecursivePtr {
|
||||
// ignore if exists recursive operation
|
||||
return
|
||||
}
|
||||
|
@ -163,7 +163,13 @@ func encodeOptimizeStructEnd(c *opcode) {
|
|||
switch code.op {
|
||||
case opStructEnd:
|
||||
prev := code.prevField
|
||||
if strings.Contains(prev.op.String(), "Head") {
|
||||
prevOp := prev.op.String()
|
||||
if strings.Contains(prevOp, "Head") ||
|
||||
strings.Contains(prevOp, "Slice") ||
|
||||
strings.Contains(prevOp, "Array") ||
|
||||
strings.Contains(prevOp, "Map") ||
|
||||
strings.Contains(prevOp, "MarshalJSON") ||
|
||||
strings.Contains(prevOp, "MarshalText") {
|
||||
// not exists field
|
||||
code = code.next
|
||||
break
|
||||
|
@ -182,96 +188,62 @@ func encodeOptimizeStructEnd(c *opcode) {
|
|||
}
|
||||
}
|
||||
|
||||
func encodeConvertHeadOnlyCode(c *opcode, isPtrHead bool) {
|
||||
if c.nextField == nil {
|
||||
return
|
||||
func encodeImplementsMarshalJSON(typ *rtype) bool {
|
||||
if !typ.Implements(marshalJSONType) {
|
||||
return false
|
||||
}
|
||||
if c.nextField.op.codeType() != codeStructEnd {
|
||||
return
|
||||
}
|
||||
switch c.op {
|
||||
case opStructFieldHead:
|
||||
encodeConvertHeadOnlyCode(c.next, false)
|
||||
if !strings.Contains(c.next.op.String(), "Only") {
|
||||
return
|
||||
}
|
||||
c.op = opStructFieldHeadOnly
|
||||
case opStructFieldHeadOmitEmpty:
|
||||
encodeConvertHeadOnlyCode(c.next, false)
|
||||
if !strings.Contains(c.next.op.String(), "Only") {
|
||||
return
|
||||
}
|
||||
c.op = opStructFieldHeadOmitEmptyOnly
|
||||
case opStructFieldHeadStringTag:
|
||||
encodeConvertHeadOnlyCode(c.next, false)
|
||||
if !strings.Contains(c.next.op.String(), "Only") {
|
||||
return
|
||||
}
|
||||
c.op = opStructFieldHeadStringTagOnly
|
||||
case opStructFieldPtrHead:
|
||||
}
|
||||
|
||||
if strings.Contains(c.op.String(), "Marshal") {
|
||||
return
|
||||
}
|
||||
if strings.Contains(c.op.String(), "Slice") {
|
||||
return
|
||||
}
|
||||
if strings.Contains(c.op.String(), "Map") {
|
||||
return
|
||||
}
|
||||
|
||||
isPtrOp := strings.Contains(c.op.String(), "Ptr")
|
||||
if isPtrOp && !isPtrHead {
|
||||
c.op = c.op.headToOnlyHead()
|
||||
} else if !isPtrOp && isPtrHead {
|
||||
c.op = c.op.headToPtrHead().headToOnlyHead()
|
||||
} else if isPtrOp && isPtrHead {
|
||||
c.op = c.op.headToPtrHead().headToOnlyHead()
|
||||
}
|
||||
}
|
||||
|
||||
func encodeImplementsMarshaler(typ *rtype) bool {
|
||||
switch {
|
||||
case typ.Implements(marshalJSONType):
|
||||
return true
|
||||
case rtype_ptrTo(typ).Implements(marshalJSONType):
|
||||
return true
|
||||
case typ.Implements(marshalTextType):
|
||||
return true
|
||||
case rtype_ptrTo(typ).Implements(marshalTextType):
|
||||
if typ.Kind() != reflect.Ptr {
|
||||
return true
|
||||
}
|
||||
// type kind is reflect.Ptr
|
||||
if !typ.Elem().Implements(marshalJSONType) {
|
||||
return true
|
||||
}
|
||||
// needs to dereference
|
||||
return false
|
||||
}
|
||||
|
||||
func encodeCompile(ctx *encodeCompileContext) (*opcode, error) {
|
||||
func encodeImplementsMarshalText(typ *rtype) bool {
|
||||
if !typ.Implements(marshalTextType) {
|
||||
return false
|
||||
}
|
||||
if typ.Kind() != reflect.Ptr {
|
||||
return true
|
||||
}
|
||||
// type kind is reflect.Ptr
|
||||
if !typ.Elem().Implements(marshalTextType) {
|
||||
return true
|
||||
}
|
||||
// needs to dereference
|
||||
return false
|
||||
}
|
||||
|
||||
func encodeCompile(ctx *encodeCompileContext, isPtr bool) (*opcode, error) {
|
||||
typ := ctx.typ
|
||||
switch {
|
||||
case typ.Implements(marshalJSONType):
|
||||
case encodeImplementsMarshalJSON(typ):
|
||||
return encodeCompileMarshalJSON(ctx)
|
||||
case rtype_ptrTo(typ).Implements(marshalJSONType):
|
||||
return encodeCompileMarshalJSONPtr(ctx)
|
||||
case typ.Implements(marshalTextType):
|
||||
case encodeImplementsMarshalText(typ):
|
||||
return encodeCompileMarshalText(ctx)
|
||||
case rtype_ptrTo(typ).Implements(marshalTextType):
|
||||
return encodeCompileMarshalTextPtr(ctx)
|
||||
}
|
||||
switch typ.Kind() {
|
||||
case reflect.Ptr:
|
||||
return encodeCompilePtr(ctx)
|
||||
case reflect.Slice:
|
||||
elem := typ.Elem()
|
||||
if !encodeImplementsMarshaler(elem) && elem.Kind() == reflect.Uint8 {
|
||||
if elem.Kind() == reflect.Uint8 {
|
||||
p := rtype_ptrTo(elem)
|
||||
if !p.Implements(marshalJSONType) && !p.Implements(marshalTextType) {
|
||||
return encodeCompileBytes(ctx)
|
||||
}
|
||||
}
|
||||
return encodeCompileSlice(ctx)
|
||||
case reflect.Array:
|
||||
return encodeCompileArray(ctx)
|
||||
case reflect.Map:
|
||||
return encodeCompileMap(ctx, true)
|
||||
return encodeCompileMap(ctx)
|
||||
case reflect.Struct:
|
||||
return encodeCompileStruct(ctx, false)
|
||||
return encodeCompileStruct(ctx, isPtr)
|
||||
case reflect.Interface:
|
||||
return encodeCompileInterface(ctx)
|
||||
case reflect.Int:
|
||||
|
@ -308,13 +280,51 @@ func encodeCompile(ctx *encodeCompileContext) (*opcode, error) {
|
|||
return nil, &UnsupportedTypeError{Type: rtype2type(typ)}
|
||||
}
|
||||
|
||||
func encodeConvertPtrOp(code *opcode) opType {
|
||||
ptrHeadOp := code.op.headToPtrHead()
|
||||
if code.op != ptrHeadOp {
|
||||
return ptrHeadOp
|
||||
}
|
||||
switch code.op {
|
||||
case opInt:
|
||||
return opIntPtr
|
||||
case opUint:
|
||||
return opUintPtr
|
||||
case opFloat32:
|
||||
return opFloat32Ptr
|
||||
case opFloat64:
|
||||
return opFloat64Ptr
|
||||
case opString:
|
||||
return opStringPtr
|
||||
case opBool:
|
||||
return opBoolPtr
|
||||
case opBytes:
|
||||
return opBytesPtr
|
||||
case opArray:
|
||||
return opArrayPtr
|
||||
case opSlice:
|
||||
return opSlicePtr
|
||||
case opMap:
|
||||
return opMapPtr
|
||||
case opMarshalJSON:
|
||||
return opMarshalJSONPtr
|
||||
case opMarshalText:
|
||||
return opMarshalTextPtr
|
||||
case opInterface:
|
||||
return opInterfacePtr
|
||||
case opStructFieldRecursive:
|
||||
return opStructFieldRecursivePtr
|
||||
}
|
||||
return code.op
|
||||
}
|
||||
|
||||
func encodeCompileKey(ctx *encodeCompileContext) (*opcode, error) {
|
||||
typ := ctx.typ
|
||||
switch {
|
||||
case rtype_ptrTo(typ).Implements(marshalJSONType):
|
||||
return encodeCompileMarshalJSONPtr(ctx)
|
||||
case rtype_ptrTo(typ).Implements(marshalTextType):
|
||||
return encodeCompileMarshalTextPtr(ctx)
|
||||
case encodeImplementsMarshalJSON(typ):
|
||||
return encodeCompileMarshalJSON(ctx)
|
||||
case encodeImplementsMarshalText(typ):
|
||||
return encodeCompileMarshalText(ctx)
|
||||
}
|
||||
switch typ.Kind() {
|
||||
case reflect.Ptr:
|
||||
|
@ -350,46 +360,31 @@ func encodeCompileKey(ctx *encodeCompileContext) (*opcode, error) {
|
|||
}
|
||||
|
||||
func encodeCompilePtr(ctx *encodeCompileContext) (*opcode, error) {
|
||||
ptrOpcodeIndex := ctx.opcodeIndex
|
||||
ptrIndex := ctx.ptrIndex
|
||||
ctx.incIndex()
|
||||
code, err := encodeCompile(ctx.withType(ctx.typ.Elem()))
|
||||
code, err := encodeCompile(ctx.withType(ctx.typ.Elem()), true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ptrHeadOp := code.op.headToPtrHead()
|
||||
if code.op != ptrHeadOp {
|
||||
code.op = ptrHeadOp
|
||||
code.decOpcodeIndex()
|
||||
ctx.decIndex()
|
||||
code.op = encodeConvertPtrOp(code)
|
||||
code.ptrNum++
|
||||
return code, nil
|
||||
}
|
||||
c := ctx.context()
|
||||
c.opcodeIndex = ptrOpcodeIndex
|
||||
c.ptrIndex = ptrIndex
|
||||
return newOpCodeWithNext(c, opPtr, code), nil
|
||||
}
|
||||
|
||||
func encodeCompileMarshalJSON(ctx *encodeCompileContext) (*opcode, error) {
|
||||
code := newOpCode(ctx, opMarshalJSON)
|
||||
ctx.incIndex()
|
||||
return code, nil
|
||||
typ := ctx.typ
|
||||
if !typ.Implements(marshalJSONType) && rtype_ptrTo(typ).Implements(marshalJSONType) {
|
||||
code.addrForMarshaler = true
|
||||
}
|
||||
|
||||
func encodeCompileMarshalJSONPtr(ctx *encodeCompileContext) (*opcode, error) {
|
||||
code := newOpCode(ctx.withType(rtype_ptrTo(ctx.typ)), opMarshalJSON)
|
||||
ctx.incIndex()
|
||||
return code, nil
|
||||
}
|
||||
|
||||
func encodeCompileMarshalText(ctx *encodeCompileContext) (*opcode, error) {
|
||||
code := newOpCode(ctx, opMarshalText)
|
||||
ctx.incIndex()
|
||||
return code, nil
|
||||
typ := ctx.typ
|
||||
if !typ.Implements(marshalTextType) && rtype_ptrTo(typ).Implements(marshalTextType) {
|
||||
code.addrForMarshaler = true
|
||||
}
|
||||
|
||||
func encodeCompileMarshalTextPtr(ctx *encodeCompileContext) (*opcode, error) {
|
||||
code := newOpCode(ctx.withType(rtype_ptrTo(ctx.typ)), opMarshalText)
|
||||
ctx.incIndex()
|
||||
return code, nil
|
||||
}
|
||||
|
@ -593,7 +588,13 @@ func encodeCompileFloat64(ctx *encodeCompileContext) (*opcode, error) {
|
|||
}
|
||||
|
||||
func encodeCompileString(ctx *encodeCompileContext) (*opcode, error) {
|
||||
code := newOpCode(ctx, opString)
|
||||
var op opType
|
||||
if ctx.typ == type2rtype(jsonNumberType) {
|
||||
op = opNumber
|
||||
} else {
|
||||
op = opString
|
||||
}
|
||||
code := newOpCode(ctx, op)
|
||||
ctx.incIndex()
|
||||
return code, nil
|
||||
}
|
||||
|
@ -617,14 +618,13 @@ func encodeCompileInterface(ctx *encodeCompileContext) (*opcode, error) {
|
|||
}
|
||||
|
||||
func encodeCompileSlice(ctx *encodeCompileContext) (*opcode, error) {
|
||||
ctx.root = false
|
||||
elem := ctx.typ.Elem()
|
||||
size := elem.Size()
|
||||
|
||||
header := newSliceHeaderCode(ctx)
|
||||
ctx.incIndex()
|
||||
|
||||
code, err := encodeCompile(ctx.withType(ctx.typ.Elem()).incIndent())
|
||||
code, err := encodeCompileSliceElem(ctx.withType(elem).incIndent())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -648,8 +648,19 @@ func encodeCompileSlice(ctx *encodeCompileContext) (*opcode, error) {
|
|||
return (*opcode)(unsafe.Pointer(header)), nil
|
||||
}
|
||||
|
||||
func encodeCompileSliceElem(ctx *encodeCompileContext) (*opcode, error) {
|
||||
typ := ctx.typ
|
||||
switch {
|
||||
case !typ.Implements(marshalJSONType) && rtype_ptrTo(typ).Implements(marshalJSONType):
|
||||
return encodeCompileMarshalJSON(ctx)
|
||||
case !typ.Implements(marshalTextType) && rtype_ptrTo(typ).Implements(marshalTextType):
|
||||
return encodeCompileMarshalText(ctx)
|
||||
default:
|
||||
return encodeCompile(ctx, false)
|
||||
}
|
||||
}
|
||||
|
||||
func encodeCompileArray(ctx *encodeCompileContext) (*opcode, error) {
|
||||
ctx.root = false
|
||||
typ := ctx.typ
|
||||
elem := typ.Elem()
|
||||
alen := typ.Len()
|
||||
|
@ -658,7 +669,7 @@ func encodeCompileArray(ctx *encodeCompileContext) (*opcode, error) {
|
|||
header := newArrayHeaderCode(ctx, alen)
|
||||
ctx.incIndex()
|
||||
|
||||
code, err := encodeCompile(ctx.withType(elem).incIndent())
|
||||
code, err := encodeCompile(ctx.withType(elem).incIndent(), false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -697,12 +708,12 @@ func mapiternext(it unsafe.Pointer)
|
|||
//go:noescape
|
||||
func maplen(m unsafe.Pointer) int
|
||||
|
||||
func encodeCompileMap(ctx *encodeCompileContext, withLoad bool) (*opcode, error) {
|
||||
func encodeCompileMap(ctx *encodeCompileContext) (*opcode, error) {
|
||||
// header => code => value => code => key => code => value => code => end
|
||||
// ^ |
|
||||
// |_______________________|
|
||||
ctx = ctx.incIndent()
|
||||
header := newMapHeaderCode(ctx, withLoad)
|
||||
header := newMapHeaderCode(ctx)
|
||||
ctx.incIndex()
|
||||
|
||||
typ := ctx.typ
|
||||
|
@ -716,7 +727,7 @@ func encodeCompileMap(ctx *encodeCompileContext, withLoad bool) (*opcode, error)
|
|||
ctx.incIndex()
|
||||
|
||||
valueType := typ.Elem()
|
||||
valueCode, err := encodeCompile(ctx.withType(valueType))
|
||||
valueCode, err := encodeCompile(ctx.withType(valueType), false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -745,174 +756,122 @@ func encodeCompileMap(ctx *encodeCompileContext, withLoad bool) (*opcode, error)
|
|||
return (*opcode)(unsafe.Pointer(header)), nil
|
||||
}
|
||||
|
||||
func encodeTypeToHeaderType(ctx *encodeCompileContext, code *opcode) opType {
|
||||
func encodeTypeToHeaderType(code *opcode) opType {
|
||||
switch code.op {
|
||||
case opPtr:
|
||||
ptrNum := 1
|
||||
c := code
|
||||
ctx.decIndex()
|
||||
for {
|
||||
if code.next.op == opPtr {
|
||||
ptrNum++
|
||||
code = code.next
|
||||
ctx.decIndex()
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
c.ptrNum = ptrNum
|
||||
if ptrNum > 1 {
|
||||
switch code.next.op {
|
||||
case opInt:
|
||||
c.mask = code.next.mask
|
||||
c.rshiftNum = code.next.rshiftNum
|
||||
return opStructFieldHeadIntNPtr
|
||||
case opUint:
|
||||
c.mask = code.next.mask
|
||||
return opStructFieldHeadUintNPtr
|
||||
case opFloat32:
|
||||
return opStructFieldHeadFloat32NPtr
|
||||
case opFloat64:
|
||||
return opStructFieldHeadFloat64NPtr
|
||||
case opString:
|
||||
return opStructFieldHeadStringNPtr
|
||||
case opBool:
|
||||
return opStructFieldHeadBoolNPtr
|
||||
}
|
||||
} else {
|
||||
switch code.next.op {
|
||||
case opInt:
|
||||
c.mask = code.next.mask
|
||||
c.rshiftNum = code.next.rshiftNum
|
||||
return opStructFieldHeadIntPtr
|
||||
case opUint:
|
||||
c.mask = code.next.mask
|
||||
return opStructFieldHeadUintPtr
|
||||
case opFloat32:
|
||||
return opStructFieldHeadFloat32Ptr
|
||||
case opFloat64:
|
||||
return opStructFieldHeadFloat64Ptr
|
||||
case opString:
|
||||
return opStructFieldHeadStringPtr
|
||||
case opBool:
|
||||
return opStructFieldHeadBoolPtr
|
||||
}
|
||||
}
|
||||
case opInt:
|
||||
return opStructFieldHeadInt
|
||||
case opIntPtr:
|
||||
return opStructFieldHeadIntPtr
|
||||
case opUint:
|
||||
return opStructFieldHeadUint
|
||||
case opUintPtr:
|
||||
return opStructFieldHeadUintPtr
|
||||
case opFloat32:
|
||||
return opStructFieldHeadFloat32
|
||||
case opFloat32Ptr:
|
||||
return opStructFieldHeadFloat32Ptr
|
||||
case opFloat64:
|
||||
return opStructFieldHeadFloat64
|
||||
case opFloat64Ptr:
|
||||
return opStructFieldHeadFloat64Ptr
|
||||
case opString:
|
||||
return opStructFieldHeadString
|
||||
case opStringPtr:
|
||||
return opStructFieldHeadStringPtr
|
||||
case opNumber:
|
||||
return opStructFieldHeadNumber
|
||||
case opNumberPtr:
|
||||
return opStructFieldHeadNumberPtr
|
||||
case opBool:
|
||||
return opStructFieldHeadBool
|
||||
case opMapHead:
|
||||
case opBoolPtr:
|
||||
return opStructFieldHeadBoolPtr
|
||||
case opMap:
|
||||
return opStructFieldHeadMap
|
||||
case opMapHeadLoad:
|
||||
return opStructFieldHeadMapLoad
|
||||
case opArrayHead:
|
||||
case opMapPtr:
|
||||
code.op = opMap
|
||||
return opStructFieldHeadMapPtr
|
||||
case opArray:
|
||||
return opStructFieldHeadArray
|
||||
case opSliceHead:
|
||||
case opArrayPtr:
|
||||
code.op = opArray
|
||||
return opStructFieldHeadArrayPtr
|
||||
case opSlice:
|
||||
return opStructFieldHeadSlice
|
||||
case opStructFieldHead:
|
||||
return opStructFieldHeadStruct
|
||||
case opSlicePtr:
|
||||
code.op = opSlice
|
||||
return opStructFieldHeadSlicePtr
|
||||
case opMarshalJSON:
|
||||
return opStructFieldHeadMarshalJSON
|
||||
case opMarshalJSONPtr:
|
||||
return opStructFieldHeadMarshalJSONPtr
|
||||
case opMarshalText:
|
||||
return opStructFieldHeadMarshalText
|
||||
case opMarshalTextPtr:
|
||||
return opStructFieldHeadMarshalTextPtr
|
||||
}
|
||||
return opStructFieldHead
|
||||
}
|
||||
|
||||
func encodeTypeToFieldType(ctx *encodeCompileContext, code *opcode) opType {
|
||||
func encodeTypeToFieldType(code *opcode) opType {
|
||||
switch code.op {
|
||||
case opPtr:
|
||||
ptrNum := 1
|
||||
ctx.decIndex()
|
||||
c := code
|
||||
for {
|
||||
if code.next.op == opPtr {
|
||||
ptrNum++
|
||||
code = code.next
|
||||
ctx.decIndex()
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
c.ptrNum = ptrNum
|
||||
if ptrNum > 1 {
|
||||
switch code.next.op {
|
||||
case opInt:
|
||||
c.mask = code.next.mask
|
||||
c.rshiftNum = code.next.rshiftNum
|
||||
return opStructFieldIntNPtr
|
||||
case opUint:
|
||||
c.mask = code.next.mask
|
||||
return opStructFieldUintNPtr
|
||||
case opFloat32:
|
||||
return opStructFieldFloat32NPtr
|
||||
case opFloat64:
|
||||
return opStructFieldFloat64NPtr
|
||||
case opString:
|
||||
return opStructFieldStringNPtr
|
||||
case opBool:
|
||||
return opStructFieldBoolNPtr
|
||||
}
|
||||
} else {
|
||||
switch code.next.op {
|
||||
case opInt:
|
||||
c.mask = code.next.mask
|
||||
c.rshiftNum = code.next.rshiftNum
|
||||
return opStructFieldIntPtr
|
||||
case opUint:
|
||||
c.mask = code.next.mask
|
||||
return opStructFieldUintPtr
|
||||
case opFloat32:
|
||||
return opStructFieldFloat32Ptr
|
||||
case opFloat64:
|
||||
return opStructFieldFloat64Ptr
|
||||
case opString:
|
||||
return opStructFieldStringPtr
|
||||
case opBool:
|
||||
return opStructFieldBoolPtr
|
||||
}
|
||||
}
|
||||
case opInt:
|
||||
return opStructFieldInt
|
||||
case opIntPtr:
|
||||
return opStructFieldIntPtr
|
||||
case opUint:
|
||||
return opStructFieldUint
|
||||
case opUintPtr:
|
||||
return opStructFieldUintPtr
|
||||
case opFloat32:
|
||||
return opStructFieldFloat32
|
||||
case opFloat32Ptr:
|
||||
return opStructFieldFloat32Ptr
|
||||
case opFloat64:
|
||||
return opStructFieldFloat64
|
||||
case opFloat64Ptr:
|
||||
return opStructFieldFloat64Ptr
|
||||
case opString:
|
||||
return opStructFieldString
|
||||
case opStringPtr:
|
||||
return opStructFieldStringPtr
|
||||
case opNumber:
|
||||
return opStructFieldNumber
|
||||
case opNumberPtr:
|
||||
return opStructFieldNumberPtr
|
||||
case opBool:
|
||||
return opStructFieldBool
|
||||
case opMapHead:
|
||||
case opBoolPtr:
|
||||
return opStructFieldBoolPtr
|
||||
case opMap:
|
||||
return opStructFieldMap
|
||||
case opMapHeadLoad:
|
||||
return opStructFieldMapLoad
|
||||
case opArrayHead:
|
||||
case opMapPtr:
|
||||
code.op = opMap
|
||||
return opStructFieldMapPtr
|
||||
case opArray:
|
||||
return opStructFieldArray
|
||||
case opSliceHead:
|
||||
case opArrayPtr:
|
||||
code.op = opArray
|
||||
return opStructFieldArrayPtr
|
||||
case opSlice:
|
||||
return opStructFieldSlice
|
||||
case opStructFieldHead:
|
||||
return opStructFieldStruct
|
||||
case opSlicePtr:
|
||||
code.op = opSlice
|
||||
return opStructFieldSlicePtr
|
||||
case opMarshalJSON:
|
||||
return opStructFieldMarshalJSON
|
||||
case opMarshalJSONPtr:
|
||||
return opStructFieldMarshalJSONPtr
|
||||
case opMarshalText:
|
||||
return opStructFieldMarshalText
|
||||
case opMarshalTextPtr:
|
||||
return opStructFieldMarshalTextPtr
|
||||
}
|
||||
return opStructField
|
||||
}
|
||||
|
||||
func encodeOptimizeStructHeader(ctx *encodeCompileContext, code *opcode, tag *structTag) opType {
|
||||
headType := encodeTypeToHeaderType(ctx, code)
|
||||
func encodeOptimizeStructHeader(code *opcode, tag *structTag) opType {
|
||||
headType := encodeTypeToHeaderType(code)
|
||||
switch {
|
||||
case tag.isOmitEmpty:
|
||||
headType = headType.headToOmitEmptyHead()
|
||||
|
@ -922,8 +881,8 @@ func encodeOptimizeStructHeader(ctx *encodeCompileContext, code *opcode, tag *st
|
|||
return headType
|
||||
}
|
||||
|
||||
func encodeOptimizeStructField(ctx *encodeCompileContext, code *opcode, tag *structTag) opType {
|
||||
fieldType := encodeTypeToFieldType(ctx, code)
|
||||
func encodeOptimizeStructField(code *opcode, tag *structTag) opType {
|
||||
fieldType := encodeTypeToFieldType(code)
|
||||
switch {
|
||||
case tag.isOmitEmpty:
|
||||
fieldType = fieldType.fieldToOmitEmptyField()
|
||||
|
@ -950,7 +909,7 @@ func encodeCompiledCode(ctx *encodeCompileContext) *opcode {
|
|||
|
||||
func encodeStructHeader(ctx *encodeCompileContext, fieldCode *opcode, valueCode *opcode, tag *structTag) *opcode {
|
||||
fieldCode.indent--
|
||||
op := encodeOptimizeStructHeader(ctx, valueCode, tag)
|
||||
op := encodeOptimizeStructHeader(valueCode, tag)
|
||||
fieldCode.op = op
|
||||
fieldCode.mask = valueCode.mask
|
||||
fieldCode.rshiftNum = valueCode.rshiftNum
|
||||
|
@ -960,16 +919,35 @@ func encodeStructHeader(ctx *encodeCompileContext, fieldCode *opcode, valueCode
|
|||
opStructFieldHeadSlice,
|
||||
opStructFieldHeadArray,
|
||||
opStructFieldHeadMap,
|
||||
opStructFieldHeadMapLoad,
|
||||
opStructFieldHeadStruct,
|
||||
opStructFieldHeadOmitEmpty,
|
||||
opStructFieldHeadOmitEmptySlice,
|
||||
opStructFieldHeadStringTagSlice,
|
||||
opStructFieldHeadOmitEmptyArray,
|
||||
opStructFieldHeadStringTagArray,
|
||||
opStructFieldHeadOmitEmptyMap,
|
||||
opStructFieldHeadOmitEmptyMapLoad,
|
||||
opStructFieldHeadStringTagMap,
|
||||
opStructFieldHeadOmitEmptyStruct,
|
||||
opStructFieldHeadStringTag:
|
||||
return valueCode.beforeLastCode()
|
||||
case opStructFieldHeadSlicePtr,
|
||||
opStructFieldHeadOmitEmptySlicePtr,
|
||||
opStructFieldHeadStringTagSlicePtr,
|
||||
opStructFieldHeadArrayPtr,
|
||||
opStructFieldHeadOmitEmptyArrayPtr,
|
||||
opStructFieldHeadStringTagArrayPtr,
|
||||
opStructFieldHeadMapPtr,
|
||||
opStructFieldHeadOmitEmptyMapPtr,
|
||||
opStructFieldHeadStringTagMapPtr:
|
||||
return valueCode.beforeLastCode()
|
||||
case opStructFieldHeadMarshalJSONPtr,
|
||||
opStructFieldHeadOmitEmptyMarshalJSONPtr,
|
||||
opStructFieldHeadStringTagMarshalJSONPtr,
|
||||
opStructFieldHeadMarshalTextPtr,
|
||||
opStructFieldHeadOmitEmptyMarshalTextPtr,
|
||||
opStructFieldHeadStringTagMarshalTextPtr:
|
||||
ctx.decOpcodeIndex()
|
||||
return (*opcode)(unsafe.Pointer(fieldCode))
|
||||
}
|
||||
ctx.decOpcodeIndex()
|
||||
return (*opcode)(unsafe.Pointer(fieldCode))
|
||||
|
@ -977,26 +955,38 @@ func encodeStructHeader(ctx *encodeCompileContext, fieldCode *opcode, valueCode
|
|||
|
||||
func encodeStructField(ctx *encodeCompileContext, fieldCode *opcode, valueCode *opcode, tag *structTag) *opcode {
|
||||
code := (*opcode)(unsafe.Pointer(fieldCode))
|
||||
op := encodeOptimizeStructField(ctx, valueCode, tag)
|
||||
op := encodeOptimizeStructField(valueCode, tag)
|
||||
fieldCode.op = op
|
||||
fieldCode.ptrNum = valueCode.ptrNum
|
||||
fieldCode.mask = valueCode.mask
|
||||
fieldCode.rshiftNum = valueCode.rshiftNum
|
||||
fieldCode.jmp = valueCode.jmp
|
||||
switch op {
|
||||
case opStructField,
|
||||
opStructFieldSlice,
|
||||
opStructFieldArray,
|
||||
opStructFieldMap,
|
||||
opStructFieldMapLoad,
|
||||
opStructFieldStruct,
|
||||
opStructFieldOmitEmpty,
|
||||
opStructFieldOmitEmptySlice,
|
||||
opStructFieldStringTagSlice,
|
||||
opStructFieldOmitEmptyArray,
|
||||
opStructFieldStringTagArray,
|
||||
opStructFieldOmitEmptyMap,
|
||||
opStructFieldOmitEmptyMapLoad,
|
||||
opStructFieldStringTagMap,
|
||||
opStructFieldOmitEmptyStruct,
|
||||
opStructFieldStringTag:
|
||||
return valueCode.beforeLastCode()
|
||||
case opStructFieldSlicePtr,
|
||||
opStructFieldOmitEmptySlicePtr,
|
||||
opStructFieldStringTagSlicePtr,
|
||||
opStructFieldArrayPtr,
|
||||
opStructFieldOmitEmptyArrayPtr,
|
||||
opStructFieldStringTagArrayPtr,
|
||||
opStructFieldMapPtr,
|
||||
opStructFieldOmitEmptyMapPtr,
|
||||
opStructFieldStringTagMapPtr:
|
||||
return valueCode.beforeLastCode()
|
||||
}
|
||||
ctx.decIndex()
|
||||
return code
|
||||
|
@ -1006,7 +996,10 @@ func encodeIsNotExistsField(head *opcode) bool {
|
|||
if head == nil {
|
||||
return false
|
||||
}
|
||||
if head.op != opStructFieldAnonymousHead {
|
||||
if head.op != opStructFieldHead {
|
||||
return false
|
||||
}
|
||||
if !head.anonymousHead {
|
||||
return false
|
||||
}
|
||||
if head.next == nil {
|
||||
|
@ -1068,14 +1061,17 @@ func encodeAnonymousStructFieldPairMap(tags structTags, named string, valueCode
|
|||
removedFields := map[*opcode]struct{}{}
|
||||
for {
|
||||
existsKey := tags.existsKey(f.displayKey)
|
||||
op := f.op.headToAnonymousHead()
|
||||
if existsKey && (f.next.op == opStructFieldPtrAnonymousHeadRecursive || f.next.op == opStructFieldAnonymousHeadRecursive) {
|
||||
isHeadOp := strings.Contains(f.op.String(), "Head")
|
||||
if existsKey && strings.Contains(f.op.String(), "Recursive") {
|
||||
// through
|
||||
} else if op != f.op {
|
||||
} else if isHeadOp && !f.anonymousHead {
|
||||
if existsKey {
|
||||
f.op = opStructFieldAnonymousHead
|
||||
// TODO: need to remove this head
|
||||
f.op = opStructFieldHead
|
||||
f.anonymousKey = true
|
||||
f.anonymousHead = true
|
||||
} else if named == "" {
|
||||
f.op = op
|
||||
f.anonymousHead = true
|
||||
}
|
||||
} else if named == "" && f.op == opStructEnd {
|
||||
f.op = opStructAnonymousEnd
|
||||
|
@ -1121,7 +1117,7 @@ func encodeAnonymousFieldPairRecursively(named string, valueCode *opcode) map[st
|
|||
f := valueCode
|
||||
var prevAnonymousField *opcode
|
||||
for {
|
||||
if f.displayKey != "" && strings.Contains(f.op.String(), "Anonymous") {
|
||||
if f.displayKey != "" && f.anonymousHead {
|
||||
key := fmt.Sprintf("%s.%s", named, f.displayKey)
|
||||
anonymousFields[key] = append(anonymousFields[key], structFieldPair{
|
||||
prevField: prevAnonymousField,
|
||||
|
@ -1158,7 +1154,9 @@ func encodeOptimizeConflictAnonymousFields(anonymousFields map[string][]structFi
|
|||
if !fieldPair.linked {
|
||||
if fieldPair.prevField == nil {
|
||||
// head operation
|
||||
fieldPair.curField.op = opStructFieldAnonymousHead
|
||||
fieldPair.curField.op = opStructFieldHead
|
||||
fieldPair.curField.anonymousHead = true
|
||||
fieldPair.curField.anonymousKey = true
|
||||
} else {
|
||||
diff := fieldPair.curField.nextField.displayIdx - fieldPair.curField.displayIdx
|
||||
for i := 0; i < diff; i++ {
|
||||
|
@ -1176,7 +1174,9 @@ func encodeOptimizeConflictAnonymousFields(anonymousFields map[string][]structFi
|
|||
if !fieldPair.linked {
|
||||
if fieldPair.prevField == nil {
|
||||
// head operation
|
||||
fieldPair.curField.op = opStructFieldAnonymousHead
|
||||
fieldPair.curField.op = opStructFieldHead
|
||||
fieldPair.curField.anonymousHead = true
|
||||
fieldPair.curField.anonymousKey = true
|
||||
} else {
|
||||
diff := fieldPair.curField.nextField.displayIdx - fieldPair.curField.displayIdx
|
||||
removedFields[fieldPair.curField] = struct{}{}
|
||||
|
@ -1196,8 +1196,22 @@ func encodeOptimizeConflictAnonymousFields(anonymousFields map[string][]structFi
|
|||
}
|
||||
}
|
||||
|
||||
func encodeIsNilableType(typ *rtype) bool {
|
||||
switch typ.Kind() {
|
||||
case reflect.Ptr:
|
||||
return true
|
||||
case reflect.Interface:
|
||||
return true
|
||||
case reflect.Slice:
|
||||
return true
|
||||
case reflect.Map:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func encodeCompileStruct(ctx *encodeCompileContext, isPtr bool) (*opcode, error) {
|
||||
ctx.root = false
|
||||
if code := encodeCompiledCode(ctx); code != nil {
|
||||
return code, nil
|
||||
}
|
||||
|
@ -1209,7 +1223,9 @@ func encodeCompileStruct(ctx *encodeCompileContext, isPtr bool) (*opcode, error)
|
|||
// ^ |
|
||||
// |__________|
|
||||
fieldNum := typ.NumField()
|
||||
indirect := ifaceIndir(typ)
|
||||
fieldIdx := 0
|
||||
disableIndirectConversion := false
|
||||
var (
|
||||
head *opcode
|
||||
code *opcode
|
||||
|
@ -1228,30 +1244,70 @@ func encodeCompileStruct(ctx *encodeCompileContext, isPtr bool) (*opcode, error)
|
|||
for i, tag := range tags {
|
||||
field := tag.field
|
||||
fieldType := type2rtype(field.Type)
|
||||
if isPtr && i == 0 {
|
||||
// head field of pointer structure at top level
|
||||
// if field type is pointer and implements MarshalJSON or MarshalText,
|
||||
// it need to operation of dereference of pointer.
|
||||
if field.Type.Kind() == reflect.Ptr &&
|
||||
(field.Type.Implements(marshalJSONType) || field.Type.Implements(marshalTextType)) {
|
||||
fieldType = rtype_ptrTo(fieldType)
|
||||
}
|
||||
}
|
||||
fieldOpcodeIndex := ctx.opcodeIndex
|
||||
fieldPtrIndex := ctx.ptrIndex
|
||||
ctx.incIndex()
|
||||
valueCode, err := encodeCompile(ctx.withType(fieldType))
|
||||
|
||||
nilcheck := true
|
||||
addrForMarshaler := false
|
||||
isIndirectSpecialCase := isPtr && i == 0 && fieldNum == 1
|
||||
isNilableType := encodeIsNilableType(fieldType)
|
||||
|
||||
var valueCode *opcode
|
||||
switch {
|
||||
case isIndirectSpecialCase && !isNilableType && encodeIsPtrMarshalJSONType(fieldType):
|
||||
// *struct{ field T } => struct { field *T }
|
||||
// func (*T) MarshalJSON() ([]byte, error)
|
||||
// move pointer position from head to first field
|
||||
code, err := encodeCompileMarshalJSON(ctx.withType(rtype_ptrTo(fieldType)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
valueCode = code
|
||||
nilcheck = false
|
||||
indirect = false
|
||||
disableIndirectConversion = true
|
||||
case isIndirectSpecialCase && !isNilableType && encodeIsPtrMarshalTextType(fieldType):
|
||||
// *struct{ field T } => struct { field *T }
|
||||
// func (*T) MarshalText() ([]byte, error)
|
||||
// move pointer position from head to first field
|
||||
code, err := encodeCompileMarshalText(ctx.withType(rtype_ptrTo(fieldType)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
valueCode = code
|
||||
nilcheck = false
|
||||
indirect = false
|
||||
disableIndirectConversion = true
|
||||
case isPtr && encodeIsPtrMarshalJSONType(fieldType):
|
||||
// *struct{ field T }
|
||||
// func (*T) MarshalJSON() ([]byte, error)
|
||||
code, err := encodeCompileMarshalJSON(ctx.withType(fieldType))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
addrForMarshaler = true
|
||||
nilcheck = false
|
||||
valueCode = code
|
||||
case isPtr && encodeIsPtrMarshalTextType(fieldType):
|
||||
// *struct{ field T }
|
||||
// func (*T) MarshalText() ([]byte, error)
|
||||
code, err := encodeCompileMarshalText(ctx.withType(fieldType))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
addrForMarshaler = true
|
||||
nilcheck = false
|
||||
valueCode = code
|
||||
default:
|
||||
code, err := encodeCompile(ctx.withType(fieldType), isPtr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
valueCode = code
|
||||
}
|
||||
|
||||
if field.Anonymous {
|
||||
if valueCode.op == opPtr && valueCode.next.op == opStructFieldRecursive {
|
||||
valueCode = valueCode.next
|
||||
valueCode.decOpcodeIndex()
|
||||
ctx.decIndex()
|
||||
valueCode.op = opStructFieldPtrHeadRecursive
|
||||
}
|
||||
tagKey := ""
|
||||
if tag.isTaggedKey {
|
||||
tagKey = tag.key
|
||||
|
@ -1259,6 +1315,14 @@ func encodeCompileStruct(ctx *encodeCompileContext, isPtr bool) (*opcode, error)
|
|||
for k, v := range encodeAnonymousStructFieldPairMap(tags, tagKey, valueCode) {
|
||||
anonymousFields[k] = append(anonymousFields[k], v...)
|
||||
}
|
||||
valueCode.decIndent()
|
||||
|
||||
// fix issue144
|
||||
if !(isPtr && strings.Contains(valueCode.op.String(), "Marshal")) {
|
||||
valueCode.indirect = indirect
|
||||
}
|
||||
} else {
|
||||
valueCode.indirect = indirect
|
||||
}
|
||||
key := fmt.Sprintf(`"%s":`, tag.key)
|
||||
escapedKey := fmt.Sprintf(`%s:`, string(encodeEscapedString([]byte{}, tag.key)))
|
||||
|
@ -1274,6 +1338,9 @@ func encodeCompileStruct(ctx *encodeCompileContext, isPtr bool) (*opcode, error)
|
|||
isTaggedKey: tag.isTaggedKey,
|
||||
displayKey: tag.key,
|
||||
offset: field.Offset,
|
||||
indirect: indirect,
|
||||
nilcheck: nilcheck,
|
||||
addrForMarshaler: addrForMarshaler,
|
||||
}
|
||||
if fieldIdx == 0 {
|
||||
fieldCode.headIdx = fieldCode.idx
|
||||
|
@ -1333,5 +1400,17 @@ func encodeCompileStruct(ctx *encodeCompileContext, isPtr bool) (*opcode, error)
|
|||
|
||||
delete(ctx.structTypeToCompiledCode, typeptr)
|
||||
|
||||
if !disableIndirectConversion && !head.indirect && isPtr {
|
||||
head.indirect = true
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func encodeIsPtrMarshalJSONType(typ *rtype) bool {
|
||||
return !typ.Implements(marshalJSONType) && rtype_ptrTo(typ).Implements(marshalJSONType)
|
||||
}
|
||||
|
||||
func encodeIsPtrMarshalTextType(typ *rtype) bool {
|
||||
return !typ.Implements(marshalTextType) && rtype_ptrTo(typ).Implements(marshalTextType)
|
||||
}
|
||||
|
|
|
@ -18,7 +18,6 @@ func encodeCompileToGetCodeSet(typeptr uintptr) (*opcodeSet, error) {
|
|||
|
||||
code, err := encodeCompileHead(&encodeCompileContext{
|
||||
typ: copiedType,
|
||||
root: true,
|
||||
structTypeToCompiledCode: map[uintptr]*compiledCode{},
|
||||
})
|
||||
if err != nil {
|
||||
|
|
|
@ -26,7 +26,6 @@ func encodeCompileToGetCodeSet(typeptr uintptr) (*opcodeSet, error) {
|
|||
|
||||
code, err := encodeCompileHead(&encodeCompileContext{
|
||||
typ: copiedType,
|
||||
root: true,
|
||||
structTypeToCompiledCode: map[uintptr]*compiledCode{},
|
||||
})
|
||||
if err != nil {
|
||||
|
|
|
@ -63,7 +63,6 @@ func releaseMapContext(c *encodeMapContext) {
|
|||
|
||||
type encodeCompileContext struct {
|
||||
typ *rtype
|
||||
root bool
|
||||
opcodeIndex int
|
||||
ptrIndex int
|
||||
indent int
|
||||
|
@ -75,7 +74,6 @@ type encodeCompileContext struct {
|
|||
func (c *encodeCompileContext) context() *encodeCompileContext {
|
||||
return &encodeCompileContext{
|
||||
typ: c.typ,
|
||||
root: c.root,
|
||||
opcodeIndex: c.opcodeIndex,
|
||||
ptrIndex: c.ptrIndex,
|
||||
indent: c.indent,
|
||||
|
|
|
@ -18,7 +18,10 @@ type opcode struct {
|
|||
displayKey string // key text to display
|
||||
isTaggedKey bool // whether tagged key
|
||||
anonymousKey bool // whether anonymous key
|
||||
root bool // whether root
|
||||
anonymousHead bool // whether anonymous head or not
|
||||
indirect bool // whether indirect or not
|
||||
nilcheck bool // whether needs to nilcheck or not
|
||||
addrForMarshaler bool // whether needs to addr for marshaler or not
|
||||
rshiftNum uint8 // use to take bit for judging whether negative integer or not
|
||||
mask uint64 // mask for number
|
||||
indent int // indent number
|
||||
|
@ -90,7 +93,10 @@ func (c *opcode) copy(codeMap map[uintptr]*opcode) *opcode {
|
|||
rshiftNum: c.rshiftNum,
|
||||
isTaggedKey: c.isTaggedKey,
|
||||
anonymousKey: c.anonymousKey,
|
||||
root: c.root,
|
||||
anonymousHead: c.anonymousHead,
|
||||
indirect: c.indirect,
|
||||
nilcheck: c.nilcheck,
|
||||
addrForMarshaler: c.addrForMarshaler,
|
||||
indent: c.indent,
|
||||
idx: c.idx,
|
||||
headIdx: c.headIdx,
|
||||
|
@ -172,6 +178,18 @@ func (c *opcode) decOpcodeIndex() {
|
|||
}
|
||||
}
|
||||
|
||||
func (c *opcode) decIndent() {
|
||||
for code := c; code.op != opEnd; {
|
||||
code.indent--
|
||||
switch code.op.codeType() {
|
||||
case codeArrayElem, codeSliceElem, codeMapKey:
|
||||
code = code.end
|
||||
default:
|
||||
code = code.next
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *opcode) dumpHead(code *opcode) string {
|
||||
var length uintptr
|
||||
if code.op.codeType() == codeArrayHead {
|
||||
|
@ -360,7 +378,7 @@ func newSliceHeaderCode(ctx *encodeCompileContext) *opcode {
|
|||
ctx.incPtrIndex()
|
||||
length := opcodeOffset(ctx.ptrIndex)
|
||||
return &opcode{
|
||||
op: opSliceHead,
|
||||
op: opSlice,
|
||||
displayIdx: ctx.opcodeIndex,
|
||||
idx: idx,
|
||||
headIdx: idx,
|
||||
|
@ -388,7 +406,7 @@ func newArrayHeaderCode(ctx *encodeCompileContext, alen int) *opcode {
|
|||
ctx.incPtrIndex()
|
||||
elemIdx := opcodeOffset(ctx.ptrIndex)
|
||||
return &opcode{
|
||||
op: opArrayHead,
|
||||
op: opArray,
|
||||
displayIdx: ctx.opcodeIndex,
|
||||
idx: idx,
|
||||
headIdx: idx,
|
||||
|
@ -406,17 +424,12 @@ func newArrayElemCode(ctx *encodeCompileContext, head *opcode, length int, size
|
|||
elemIdx: head.elemIdx,
|
||||
headIdx: head.headIdx,
|
||||
length: uintptr(length),
|
||||
indent: ctx.indent,
|
||||
size: size,
|
||||
}
|
||||
}
|
||||
|
||||
func newMapHeaderCode(ctx *encodeCompileContext, withLoad bool) *opcode {
|
||||
var op opType
|
||||
if withLoad {
|
||||
op = opMapHeadLoad
|
||||
} else {
|
||||
op = opMapHead
|
||||
}
|
||||
func newMapHeaderCode(ctx *encodeCompileContext) *opcode {
|
||||
idx := opcodeOffset(ctx.ptrIndex)
|
||||
ctx.incPtrIndex()
|
||||
elemIdx := opcodeOffset(ctx.ptrIndex)
|
||||
|
@ -425,7 +438,7 @@ func newMapHeaderCode(ctx *encodeCompileContext, withLoad bool) *opcode {
|
|||
ctx.incPtrIndex()
|
||||
mapIter := opcodeOffset(ctx.ptrIndex)
|
||||
return &opcode{
|
||||
op: op,
|
||||
op: opMap,
|
||||
typ: ctx.typ,
|
||||
displayIdx: ctx.opcodeIndex,
|
||||
idx: idx,
|
||||
|
@ -482,7 +495,6 @@ func newInterfaceCode(ctx *encodeCompileContext) *opcode {
|
|||
displayIdx: ctx.opcodeIndex,
|
||||
idx: opcodeOffset(ctx.ptrIndex),
|
||||
indent: ctx.indent,
|
||||
root: ctx.root,
|
||||
next: newEndOp(ctx),
|
||||
}
|
||||
}
|
||||
|
|
3174
encode_optype.go
3174
encode_optype.go
File diff suppressed because it is too large
Load Diff
115
encode_test.go
115
encode_test.go
|
@ -3,6 +3,7 @@ package json_test
|
|||
import (
|
||||
"bytes"
|
||||
"encoding"
|
||||
stdjson "encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
|
@ -1674,3 +1675,117 @@ func TestOmitEmpty(t *testing.T) {
|
|||
t.Errorf(" got: %s\nwant: %s\n", got, optionalsExpected)
|
||||
}
|
||||
}
|
||||
|
||||
type testNullStr string
|
||||
|
||||
func (v *testNullStr) MarshalJSON() ([]byte, error) {
|
||||
if *v == "" {
|
||||
return []byte("null"), nil
|
||||
}
|
||||
|
||||
return []byte(*v), nil
|
||||
}
|
||||
|
||||
func TestIssue147(t *testing.T) {
|
||||
type T struct {
|
||||
Field1 string `json:"field1"`
|
||||
Field2 testNullStr `json:"field2,omitempty"`
|
||||
}
|
||||
got, err := json.Marshal(T{
|
||||
Field1: "a",
|
||||
Field2: "b",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
expect, _ := stdjson.Marshal(T{
|
||||
Field1: "a",
|
||||
Field2: "b",
|
||||
})
|
||||
if !bytes.Equal(expect, got) {
|
||||
t.Fatalf("expect %q but got %q", string(expect), string(got))
|
||||
}
|
||||
}
|
||||
|
||||
type testIssue144 struct {
|
||||
name string
|
||||
number int64
|
||||
}
|
||||
|
||||
func (v *testIssue144) MarshalJSON() ([]byte, error) {
|
||||
if v.name != "" {
|
||||
return json.Marshal(v.name)
|
||||
}
|
||||
return json.Marshal(v.number)
|
||||
}
|
||||
|
||||
func TestIssue144(t *testing.T) {
|
||||
type Embeded struct {
|
||||
Field *testIssue144 `json:"field,omitempty"`
|
||||
}
|
||||
type T struct {
|
||||
Embeded
|
||||
}
|
||||
{
|
||||
v := T{
|
||||
Embeded: Embeded{Field: &testIssue144{name: "hoge"}},
|
||||
}
|
||||
got, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
expect, _ := stdjson.Marshal(v)
|
||||
if !bytes.Equal(expect, got) {
|
||||
t.Fatalf("expect %q but got %q", string(expect), string(got))
|
||||
}
|
||||
}
|
||||
{
|
||||
v := &T{
|
||||
Embeded: Embeded{Field: &testIssue144{name: "hoge"}},
|
||||
}
|
||||
got, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
expect, _ := stdjson.Marshal(v)
|
||||
if !bytes.Equal(expect, got) {
|
||||
t.Fatalf("expect %q but got %q", string(expect), string(got))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIssue118(t *testing.T) {
|
||||
type data struct {
|
||||
Columns []string `json:"columns"`
|
||||
Rows1 [][]string `json:"rows1"`
|
||||
Rows2 [][]string `json:"rows2"`
|
||||
}
|
||||
v := data{Columns: []string{"1", "2", "3"}}
|
||||
got, err := json.MarshalIndent(v, "", " ")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
expect, _ := stdjson.MarshalIndent(v, "", " ")
|
||||
if !bytes.Equal(expect, got) {
|
||||
t.Fatalf("expect %q but got %q", string(expect), string(got))
|
||||
}
|
||||
}
|
||||
|
||||
func TestIssue104(t *testing.T) {
|
||||
type A struct {
|
||||
Field1 string
|
||||
Field2 int
|
||||
Field3 float64
|
||||
}
|
||||
type T struct {
|
||||
Field A
|
||||
}
|
||||
got, err := json.Marshal(T{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
expect, _ := stdjson.Marshal(T{})
|
||||
if !bytes.Equal(expect, got) {
|
||||
t.Fatalf("expect %q but got %q", string(expect), string(got))
|
||||
}
|
||||
}
|
||||
|
|
5057
encode_vm.go
5057
encode_vm.go
File diff suppressed because it is too large
Load Diff
5188
encode_vm_escaped.go
5188
encode_vm_escaped.go
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
4802
encode_vm_indent.go
4802
encode_vm_indent.go
File diff suppressed because it is too large
Load Diff
36
json.go
36
json.go
|
@ -2,8 +2,8 @@ package json
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Marshaler is the interface implemented by types that
|
||||
|
@ -281,39 +281,7 @@ func UnmarshalNoEscape(data []byte, v interface{}) error {
|
|||
type Token interface{}
|
||||
|
||||
// A Number represents a JSON number literal.
|
||||
type Number string
|
||||
|
||||
// String returns the literal text of the number.
|
||||
func (n Number) String() string { return string(n) }
|
||||
|
||||
// Float64 returns the number as a float64.
|
||||
func (n Number) Float64() (float64, error) {
|
||||
return strconv.ParseFloat(string(n), 64)
|
||||
}
|
||||
|
||||
// Int64 returns the number as an int64.
|
||||
func (n Number) Int64() (int64, error) {
|
||||
return strconv.ParseInt(string(n), 10, 64)
|
||||
}
|
||||
|
||||
func (n Number) MarshalJSON() ([]byte, error) {
|
||||
if n == "" {
|
||||
return []byte("0"), nil
|
||||
}
|
||||
if _, err := n.Float64(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return []byte(n), nil
|
||||
}
|
||||
|
||||
func (n *Number) UnmarshalJSON(b []byte) error {
|
||||
s := string(b)
|
||||
if _, err := strconv.ParseFloat(s, 64); err != nil {
|
||||
return &SyntaxError{msg: err.Error()}
|
||||
}
|
||||
*n = Number(s)
|
||||
return nil
|
||||
}
|
||||
type Number = json.Number
|
||||
|
||||
// RawMessage is a raw encoded JSON value.
|
||||
// It implements Marshaler and Unmarshaler and can
|
||||
|
|
Loading…
Reference in New Issue