forked from mirror/go-json
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,102 +262,53 @@ 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 _, opt := range []string{"", "OmitEmpty", "StringTag"} {
|
||||
for _, onlyOrNot := range []string{"", "Only"} {
|
||||
ptrOrNot := ptrOrNot
|
||||
headType := headType
|
||||
opt := opt
|
||||
typ := typ
|
||||
onlyOrNot := onlyOrNot
|
||||
for _, ptrOrNot := range []string{"", "Ptr"} {
|
||||
for _, opt := range []string{"", "OmitEmpty", "StringTag"} {
|
||||
ptrOrNot := ptrOrNot
|
||||
opt := opt
|
||||
typ := typ
|
||||
|
||||
op := fmt.Sprintf(
|
||||
"StructField%s%sHead%s%s%s",
|
||||
ptrOrNot,
|
||||
headType,
|
||||
op := fmt.Sprintf(
|
||||
"StructField%sHead%s%s",
|
||||
ptrOrNot,
|
||||
opt,
|
||||
typ,
|
||||
)
|
||||
opTypes = append(opTypes, opType{
|
||||
Op: op,
|
||||
Code: "StructField",
|
||||
HeadToPtrHead: func() string {
|
||||
return fmt.Sprintf(
|
||||
"StructFieldPtrHead%s%s",
|
||||
opt,
|
||||
typ,
|
||||
onlyOrNot,
|
||||
)
|
||||
opTypes = append(opTypes, opType{
|
||||
Op: op,
|
||||
Code: "StructField",
|
||||
HeadToPtrHead: func() string {
|
||||
return fmt.Sprintf(
|
||||
"StructFieldPtr%sHead%s%s%s",
|
||||
headType,
|
||||
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",
|
||||
ptrOrNot,
|
||||
headType,
|
||||
typ,
|
||||
onlyOrNot,
|
||||
)
|
||||
},
|
||||
HeadToStringTagHead: func() string {
|
||||
return fmt.Sprintf(
|
||||
"StructField%s%sHeadStringTag%s%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,
|
||||
opt,
|
||||
typ,
|
||||
onlyOrNot,
|
||||
)
|
||||
},
|
||||
FieldToEnd: func() string { return op },
|
||||
FieldToOmitEmptyField: func() string { return op },
|
||||
FieldToStringTagField: func() string { return op },
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
HeadToOmitEmptyHead: func() string {
|
||||
return fmt.Sprintf(
|
||||
"StructField%sHeadOmitEmpty%s",
|
||||
ptrOrNot,
|
||||
typ,
|
||||
)
|
||||
},
|
||||
HeadToStringTagHead: func() string {
|
||||
return fmt.Sprintf(
|
||||
"StructField%sHeadStringTag%s",
|
||||
ptrOrNot,
|
||||
typ,
|
||||
)
|
||||
},
|
||||
PtrHeadToHead: func() string {
|
||||
return fmt.Sprintf(
|
||||
"StructFieldHead%s%s",
|
||||
opt,
|
||||
typ,
|
||||
)
|
||||
},
|
||||
FieldToEnd: func() string { return op },
|
||||
FieldToOmitEmptyField: func() string { return op },
|
||||
FieldToStringTagField: func() string { return op },
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,20 +5,23 @@ import (
|
|||
stdjson "encoding/json"
|
||||
)
|
||||
|
||||
func intptr(v int) *int { return &v }
|
||||
func int8ptr(v int8) *int8 { return &v }
|
||||
func int16ptr(v int16) *int16 { return &v }
|
||||
func int32ptr(v int32) *int32 { return &v }
|
||||
func int64ptr(v int64) *int64 { return &v }
|
||||
func uptr(v uint) *uint { return &v }
|
||||
func uint8ptr(v uint8) *uint8 { return &v }
|
||||
func uint16ptr(v uint16) *uint16 { return &v }
|
||||
func uint32ptr(v uint32) *uint32 { return &v }
|
||||
func uint64ptr(v uint64) *uint64 { return &v }
|
||||
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 intptr(v int) *int { return &v }
|
||||
func int8ptr(v int8) *int8 { return &v }
|
||||
func int16ptr(v int16) *int16 { return &v }
|
||||
func int32ptr(v int32) *int32 { return &v }
|
||||
func int64ptr(v int64) *int64 { return &v }
|
||||
func uptr(v uint) *uint { return &v }
|
||||
func uint8ptr(v uint8) *uint8 { return &v }
|
||||
func uint16ptr(v uint16) *uint16 { return &v }
|
||||
func uint32ptr(v uint32) *uint32 { return &v }
|
||||
func uint64ptr(v uint64) *uint64 { return &v }
|
||||
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,42 +1,118 @@
|
|||
package json
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type numberDecoder struct {
|
||||
*floatDecoder
|
||||
op func(unsafe.Pointer, Number)
|
||||
structName string
|
||||
fieldName string
|
||||
stringDecoder *stringDecoder
|
||||
op func(unsafe.Pointer, Number)
|
||||
structName string
|
||||
fieldName string
|
||||
}
|
||||
|
||||
func newNumberDecoder(structName, fieldName string, op func(unsafe.Pointer, Number)) *numberDecoder {
|
||||
return &numberDecoder{
|
||||
floatDecoder: newFloatDecoder(structName, fieldName, nil),
|
||||
op: op,
|
||||
structName: structName,
|
||||
fieldName: fieldName,
|
||||
stringDecoder: newStringDecoder(structName, fieldName),
|
||||
op: op,
|
||||
structName: structName,
|
||||
fieldName: fieldName,
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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,
|
||||
|
|
102
encode_opcode.go
102
encode_opcode.go
|
@ -9,19 +9,22 @@ import (
|
|||
const uintptrSize = 4 << (^uintptr(0) >> 63) // unsafe.Sizeof(uintptr(0)) but an ideal const
|
||||
|
||||
type opcode struct {
|
||||
op opType // operation type
|
||||
typ *rtype // go type
|
||||
displayIdx int // opcode index
|
||||
key []byte // struct field key
|
||||
escapedKey []byte // struct field key ( HTML escaped )
|
||||
ptrNum int // pointer number: e.g. double pointer is 2.
|
||||
displayKey string // key text to display
|
||||
isTaggedKey bool // whether tagged key
|
||||
anonymousKey bool // whether anonymous key
|
||||
root bool // whether root
|
||||
rshiftNum uint8 // use to take bit for judging whether negative integer or not
|
||||
mask uint64 // mask for number
|
||||
indent int // indent number
|
||||
op opType // operation type
|
||||
typ *rtype // go type
|
||||
displayIdx int // opcode index
|
||||
key []byte // struct field key
|
||||
escapedKey []byte // struct field key ( HTML escaped )
|
||||
ptrNum int // pointer number: e.g. double pointer is 2.
|
||||
displayKey string // key text to display
|
||||
isTaggedKey bool // whether tagged key
|
||||
anonymousKey bool // whether anonymous key
|
||||
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
|
||||
|
||||
idx uintptr // offset to access ptr
|
||||
headIdx uintptr // offset to access slice/struct head
|
||||
|
@ -79,27 +82,30 @@ func (c *opcode) copy(codeMap map[uintptr]*opcode) *opcode {
|
|||
return code
|
||||
}
|
||||
copied := &opcode{
|
||||
op: c.op,
|
||||
typ: c.typ,
|
||||
displayIdx: c.displayIdx,
|
||||
key: c.key,
|
||||
escapedKey: c.escapedKey,
|
||||
displayKey: c.displayKey,
|
||||
ptrNum: c.ptrNum,
|
||||
mask: c.mask,
|
||||
rshiftNum: c.rshiftNum,
|
||||
isTaggedKey: c.isTaggedKey,
|
||||
anonymousKey: c.anonymousKey,
|
||||
root: c.root,
|
||||
indent: c.indent,
|
||||
idx: c.idx,
|
||||
headIdx: c.headIdx,
|
||||
elemIdx: c.elemIdx,
|
||||
length: c.length,
|
||||
mapIter: c.mapIter,
|
||||
mapPos: c.mapPos,
|
||||
offset: c.offset,
|
||||
size: c.size,
|
||||
op: c.op,
|
||||
typ: c.typ,
|
||||
displayIdx: c.displayIdx,
|
||||
key: c.key,
|
||||
escapedKey: c.escapedKey,
|
||||
displayKey: c.displayKey,
|
||||
ptrNum: c.ptrNum,
|
||||
mask: c.mask,
|
||||
rshiftNum: c.rshiftNum,
|
||||
isTaggedKey: c.isTaggedKey,
|
||||
anonymousKey: c.anonymousKey,
|
||||
anonymousHead: c.anonymousHead,
|
||||
indirect: c.indirect,
|
||||
nilcheck: c.nilcheck,
|
||||
addrForMarshaler: c.addrForMarshaler,
|
||||
indent: c.indent,
|
||||
idx: c.idx,
|
||||
headIdx: c.headIdx,
|
||||
elemIdx: c.elemIdx,
|
||||
length: c.length,
|
||||
mapIter: c.mapIter,
|
||||
mapPos: c.mapPos,
|
||||
offset: c.offset,
|
||||
size: c.size,
|
||||
}
|
||||
codeMap[addr] = copied
|
||||
copied.mapKey = c.mapKey.copy(codeMap)
|
||||
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
|
3180
encode_optype.go
3180
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))
|
||||
}
|
||||
}
|
||||
|
|
5529
encode_vm.go
5529
encode_vm.go
File diff suppressed because it is too large
Load Diff
5638
encode_vm_escaped.go
5638
encode_vm_escaped.go
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
5226
encode_vm_indent.go
5226
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