Merge pull request #31 from goccy/feature/support-bytes

Support encoding of []byte type
This commit is contained in:
Masaaki Goshima 2020-08-19 10:52:58 +09:00 committed by GitHub
commit 4e3378926b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 822 additions and 372 deletions

View File

@ -171,7 +171,8 @@ func (t opType) fieldToOmitEmptyField() opType {
primitiveTypes := []string{ primitiveTypes := []string{
"int", "int8", "int16", "int32", "int64", "int", "int8", "int16", "int32", "int64",
"uint", "uint8", "uint16", "uint32", "uint64", "uint", "uint8", "uint16", "uint32", "uint64",
"float32", "float64", "bool", "string", "MarshalJSON", "MarshalText", "float32", "float64", "bool", "string", "bytes",
"MarshalJSON", "MarshalText",
} }
primitiveTypesUpper := []string{} primitiveTypesUpper := []string{}
for _, typ := range primitiveTypes { for _, typ := range primitiveTypes {

View File

@ -47,6 +47,9 @@ func (e *Encoder) compile(typ *rtype, root, withIndent bool) (*opcode, error) {
case reflect.Ptr: case reflect.Ptr:
return e.compilePtr(typ, root, withIndent) return e.compilePtr(typ, root, withIndent)
case reflect.Slice: case reflect.Slice:
if typ.Elem().Kind() == reflect.Uint8 {
return e.compileBytes(typ)
}
return e.compileSlice(typ, root, withIndent) return e.compileSlice(typ, root, withIndent)
case reflect.Array: case reflect.Array:
return e.compileArray(typ, root, withIndent) return e.compileArray(typ, root, withIndent)
@ -163,6 +166,10 @@ func (e *Encoder) compileBool(typ *rtype) (*opcode, error) {
return newOpCode(opBool, typ, e.indent, newEndOp(e.indent)), nil return newOpCode(opBool, typ, e.indent, newEndOp(e.indent)), nil
} }
func (e *Encoder) compileBytes(typ *rtype) (*opcode, error) {
return newOpCode(opBytes, typ, e.indent, newEndOp(e.indent)), nil
}
func (e *Encoder) compileInterface(typ *rtype, root bool) (*opcode, error) { func (e *Encoder) compileInterface(typ *rtype, root bool) (*opcode, error) {
return (*opcode)(unsafe.Pointer(&interfaceCode{ return (*opcode)(unsafe.Pointer(&interfaceCode{
opcodeHeader: &opcodeHeader{ opcodeHeader: &opcodeHeader{

File diff suppressed because it is too large Load Diff

View File

@ -492,6 +492,31 @@ func Test_MarshalIndent(t *testing.T) {
}) })
} }
// byte slices are special even if they're renamed types.
type renamedByte byte
type renamedByteSlice []byte
type renamedRenamedByteSlice []renamedByte
func TestEncodeRenamedByteSlice(t *testing.T) {
s := renamedByteSlice("abc")
result, err := json.Marshal(s)
if err != nil {
t.Fatal(err)
}
expect := `"YWJj"`
if string(result) != expect {
t.Errorf(" got %s want %s", result, expect)
}
r := renamedRenamedByteSlice("abc")
result, err = json.Marshal(r)
if err != nil {
t.Fatal(err)
}
if string(result) != expect {
t.Errorf(" got %s want %s", result, expect)
}
}
func TestMarshalRawMessageValue(t *testing.T) { func TestMarshalRawMessageValue(t *testing.T) {
type ( type (
T1 struct { T1 struct {

View File

@ -3,6 +3,7 @@ package json
import ( import (
"bytes" "bytes"
"encoding" "encoding"
"encoding/base64"
"fmt" "fmt"
"math" "math"
"reflect" "reflect"
@ -66,6 +67,12 @@ func (e *Encoder) run(code *opcode) error {
case opBool: case opBool:
e.encodeBool(e.ptrToBool(code.ptr)) e.encodeBool(e.ptrToBool(code.ptr))
code = code.next code = code.next
case opBytes:
s := base64.StdEncoding.EncodeToString(e.ptrToBytes(code.ptr))
e.encodeByte('"')
e.encodeBytes(*(*[]byte)(unsafe.Pointer(&s)))
e.encodeByte('"')
code = code.next
case opInterface: case opInterface:
ifaceCode := code.toInterfaceCode() ifaceCode := code.toInterfaceCode()
ptr := ifaceCode.ptr ptr := ifaceCode.ptr
@ -940,6 +947,42 @@ func (e *Encoder) run(code *opcode) error {
field.nextField.ptr = ptr field.nextField.ptr = ptr
code = field.next code = field.next
} }
case opStructFieldPtrHeadBytes:
code.ptr = e.ptrToPtr(code.ptr)
fallthrough
case opStructFieldHeadBytes:
field := code.toStructFieldCode()
ptr := field.ptr
if ptr == 0 {
e.encodeNull()
code = field.end
} else {
e.encodeByte('{')
e.encodeBytes(field.key)
s := base64.StdEncoding.EncodeToString(e.ptrToBytes(ptr))
e.encodeByte('"')
e.encodeBytes(*(*[]byte)(unsafe.Pointer(&s)))
e.encodeByte('"')
field.nextField.ptr = ptr
code = field.next
}
case opStructFieldPtrAnonymousHeadBytes:
code.ptr = e.ptrToPtr(code.ptr)
fallthrough
case opStructFieldAnonymousHeadBytes:
field := code.toStructFieldCode()
ptr := field.ptr
if ptr == 0 {
code = field.end
} else {
e.encodeBytes(field.key)
s := base64.StdEncoding.EncodeToString(e.ptrToBytes(code.ptr))
e.encodeByte('"')
e.encodeBytes(*(*[]byte)(unsafe.Pointer(&s)))
e.encodeByte('"')
field.nextField.ptr = ptr
code = field.next
}
case opStructFieldPtrHeadMarshalJSON: case opStructFieldPtrHeadMarshalJSON:
code.ptr = e.ptrToPtr(code.ptr) code.ptr = e.ptrToPtr(code.ptr)
fallthrough fallthrough
@ -1371,6 +1414,29 @@ func (e *Encoder) run(code *opcode) error {
field.nextField.ptr = ptr field.nextField.ptr = ptr
code = field.next code = field.next
} }
case opStructFieldPtrHeadBytesIndent:
code.ptr = e.ptrToPtr(code.ptr)
fallthrough
case opStructFieldHeadBytesIndent:
field := code.toStructFieldCode()
ptr := field.ptr
if ptr == 0 {
e.encodeIndent(code.indent)
e.encodeNull()
code = field.end
} else {
e.encodeIndent(code.indent)
e.encodeBytes([]byte{'{', '\n'})
e.encodeIndent(code.indent + 1)
e.encodeBytes(field.key)
e.encodeByte(' ')
s := base64.StdEncoding.EncodeToString(e.ptrToBytes(ptr))
e.encodeByte('"')
e.encodeBytes(*(*[]byte)(unsafe.Pointer(&s)))
e.encodeByte('"')
field.nextField.ptr = ptr
code = field.next
}
case opStructFieldPtrHeadOmitEmpty: case opStructFieldPtrHeadOmitEmpty:
if code.ptr != 0 { if code.ptr != 0 {
code.ptr = e.ptrToPtr(code.ptr) code.ptr = e.ptrToPtr(code.ptr)
@ -2043,7 +2109,56 @@ func (e *Encoder) run(code *opcode) error {
} }
field.nextField.ptr = ptr field.nextField.ptr = ptr
} }
case opStructFieldPtrHeadOmitEmptyBytes:
if code.ptr != 0 {
code.ptr = e.ptrToPtr(code.ptr)
}
fallthrough
case opStructFieldHeadOmitEmptyBytes:
field := code.toStructFieldCode()
ptr := field.ptr
if ptr == 0 {
e.encodeNull()
code = field.end.next
} else {
e.encodeByte('{')
v := e.ptrToBytes(ptr + field.offset)
if len(v) == 0 {
code = field.nextField
} else {
e.encodeBytes(field.key)
s := base64.StdEncoding.EncodeToString(v)
e.encodeByte('"')
e.encodeBytes(*(*[]byte)(unsafe.Pointer(&s)))
e.encodeByte('"')
code = field.next
}
field.nextField.ptr = ptr
}
case opStructFieldPtrAnonymousHeadOmitEmptyBytes:
if code.ptr != 0 {
code.ptr = e.ptrToPtr(code.ptr)
}
fallthrough
case opStructFieldAnonymousHeadOmitEmptyBytes:
field := code.toStructFieldCode()
ptr := field.ptr
if ptr == 0 {
code = field.end.next
} else {
v := e.ptrToBytes(ptr + field.offset)
if len(v) == 0 {
code = field.nextField
} else {
e.encodeBytes(field.key)
s := base64.StdEncoding.EncodeToString(v)
e.encodeByte('"')
e.encodeBytes(*(*[]byte)(unsafe.Pointer(&s)))
e.encodeByte('"')
code = field.next
}
field.nextField.ptr = ptr
}
case opStructFieldPtrHeadOmitEmptyMarshalJSON: case opStructFieldPtrHeadOmitEmptyMarshalJSON:
if code.ptr != 0 { if code.ptr != 0 {
code.ptr = e.ptrToPtr(code.ptr) code.ptr = e.ptrToPtr(code.ptr)
@ -2607,6 +2722,36 @@ func (e *Encoder) run(code *opcode) error {
} }
field.nextField.ptr = field.ptr field.nextField.ptr = field.ptr
} }
case opStructFieldPtrHeadOmitEmptyBytesIndent:
if code.ptr != 0 {
code.ptr = e.ptrToPtr(code.ptr)
}
fallthrough
case opStructFieldHeadOmitEmptyBytesIndent:
field := code.toStructFieldCode()
ptr := field.ptr
if ptr == 0 {
e.encodeIndent(code.indent)
e.encodeNull()
code = field.end.next
} else {
e.encodeIndent(code.indent)
e.encodeBytes([]byte{'{', '\n'})
v := e.ptrToBytes(ptr + field.offset)
if len(v) == 0 {
code = field.nextField
} else {
e.encodeIndent(code.indent + 1)
e.encodeBytes(field.key)
e.encodeByte(' ')
s := base64.StdEncoding.EncodeToString(v)
e.encodeByte('"')
e.encodeBytes(*(*[]byte)(unsafe.Pointer(&s)))
e.encodeByte('"')
code = field.next
}
field.nextField.ptr = field.ptr
}
case opStructField: case opStructField:
if e.buf[len(e.buf)-1] != '{' { if e.buf[len(e.buf)-1] != '{' {
e.encodeByte(',') e.encodeByte(',')
@ -2749,6 +2894,18 @@ func (e *Encoder) run(code *opcode) error {
e.encodeBytes(c.key) e.encodeBytes(c.key)
e.encodeBool(e.ptrToBool(c.ptr + c.offset)) e.encodeBool(e.ptrToBool(c.ptr + c.offset))
code = code.next code = code.next
case opStructFieldBytes:
if e.buf[len(e.buf)-1] != '{' {
e.encodeByte(',')
}
c := code.toStructFieldCode()
c.nextField.ptr = c.ptr
e.encodeBytes(c.key)
s := base64.StdEncoding.EncodeToString(e.ptrToBytes(c.ptr + c.offset))
e.encodeByte('"')
e.encodeBytes(*(*[]byte)(unsafe.Pointer(&s)))
e.encodeByte('"')
code = code.next
case opStructFieldMarshalJSON: case opStructFieldMarshalJSON:
if e.buf[len(e.buf)-1] != '{' { if e.buf[len(e.buf)-1] != '{' {
e.encodeByte(',') e.encodeByte(',')
@ -2967,6 +3124,20 @@ func (e *Encoder) run(code *opcode) error {
e.encodeBool(e.ptrToBool(c.ptr + c.offset)) e.encodeBool(e.ptrToBool(c.ptr + c.offset))
code = code.next code = code.next
c.nextField.ptr = c.ptr c.nextField.ptr = c.ptr
case opStructFieldBytesIndent:
c := code.toStructFieldCode()
if e.buf[len(e.buf)-2] != '{' {
e.encodeBytes([]byte{',', '\n'})
}
e.encodeIndent(c.indent)
e.encodeBytes(c.key)
e.encodeByte(' ')
s := base64.StdEncoding.EncodeToString(e.ptrToBytes(c.ptr + c.offset))
e.encodeByte('"')
e.encodeBytes(*(*[]byte)(unsafe.Pointer(&s)))
e.encodeByte('"')
code = code.next
c.nextField.ptr = c.ptr
case opStructFieldOmitEmpty: case opStructFieldOmitEmpty:
c := code.toStructFieldCode() c := code.toStructFieldCode()
p := c.ptr + c.offset p := c.ptr + c.offset
@ -3155,7 +3326,21 @@ func (e *Encoder) run(code *opcode) error {
} }
code = code.next code = code.next
code.ptr = c.ptr code.ptr = c.ptr
case opStructFieldOmitEmptyBytes:
c := code.toStructFieldCode()
v := e.ptrToBytes(c.ptr + c.offset)
if len(v) > 0 {
if e.buf[len(e.buf)-1] != '{' {
e.encodeByte(',')
}
e.encodeBytes(c.key)
s := base64.StdEncoding.EncodeToString(v)
e.encodeByte('"')
e.encodeBytes(*(*[]byte)(unsafe.Pointer(&s)))
e.encodeByte('"')
}
code = code.next
code.ptr = c.ptr
case opStructFieldOmitEmptyMarshalJSON: case opStructFieldOmitEmptyMarshalJSON:
c := code.toStructFieldCode() c := code.toStructFieldCode()
ptr := c.ptr + c.offset ptr := c.ptr + c.offset
@ -3420,6 +3605,24 @@ func (e *Encoder) run(code *opcode) error {
} }
code = code.next code = code.next
code.ptr = c.ptr code.ptr = c.ptr
case opStructFieldOmitEmptyBytesIndent:
c := code.toStructFieldCode()
v := e.ptrToBytes(c.ptr + c.offset)
if len(v) > 0 {
if e.buf[len(e.buf)-2] != '{' {
e.encodeBytes([]byte{',', '\n'})
}
e.encodeIndent(c.indent)
e.encodeBytes(c.key)
e.encodeByte(' ')
s := base64.StdEncoding.EncodeToString(v)
e.encodeByte('"')
e.encodeBytes(*(*[]byte)(unsafe.Pointer(&s)))
e.encodeByte('"')
}
code = code.next
code.ptr = c.ptr
case opStructEnd: case opStructEnd:
e.encodeByte('}') e.encodeByte('}')
code = code.next code = code.next