mirror of
https://github.com/ii64/sonic.git
synced 2026-06-21 00:46:43 +08:00
fix:(ast) quote string when node.MarshalJSON() (#155)
Co-authored-by: duanyi.aster <duanyi.aster@bytedance.com>
This commit is contained in:
parent
8405d84e31
commit
e80837a84d
2 changed files with 71 additions and 5 deletions
|
|
@ -17,13 +17,17 @@
|
|||
package ast
|
||||
|
||||
import (
|
||||
`reflect`
|
||||
`sync`
|
||||
`unsafe`
|
||||
|
||||
`github.com/bytedance/sonic/encoder`
|
||||
`github.com/bytedance/sonic/internal/native`
|
||||
`github.com/bytedance/sonic/internal/rt`
|
||||
)
|
||||
|
||||
const (
|
||||
_MaxBuffer = 4 * 1024 // 4KB buffer size
|
||||
_MaxBuffer = 1024 // 1KB buffer size
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -113,10 +117,45 @@ func (self *Node) encodeNumber(buf *[]byte) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
var typeByte = rt.UnpackType(reflect.TypeOf(byte(0)))
|
||||
|
||||
func quote(buf *[]byte, sp unsafe.Pointer, nb int) {
|
||||
b := (*rt.GoSlice)(unsafe.Pointer(buf))
|
||||
// input buffer
|
||||
for nb > 0 {
|
||||
// output buffer
|
||||
dp := unsafe.Pointer(uintptr(b.Ptr) + uintptr(b.Len))
|
||||
dn := b.Cap - b.Len
|
||||
// call native.Quote, dn is byte count it outputs
|
||||
ret := native.Quote(sp, nb, dp, &dn, 0)
|
||||
// update *buf length
|
||||
b.Len += dn
|
||||
|
||||
// no need more output
|
||||
if ret >= 0 {
|
||||
break
|
||||
}
|
||||
|
||||
// double buf size
|
||||
*b = growslice(typeByte, *b, b.Cap * 2)
|
||||
// ret is the complement of consumed input
|
||||
ret = ^ret
|
||||
// update input buffer
|
||||
nb -= ret
|
||||
sp = unsafe.Pointer(uintptr(sp) + uintptr(ret))
|
||||
}
|
||||
}
|
||||
|
||||
func (self *Node) encodeString(buf *[]byte) error {
|
||||
str := addr2str(self.p, self.v)
|
||||
*buf = append(*buf, '"')
|
||||
*buf = append(*buf, str...)
|
||||
nb := int(self.v)
|
||||
if nb == 0 {
|
||||
*buf = append(*buf, '"')
|
||||
return nil
|
||||
}
|
||||
|
||||
quote(buf, self.p, nb)
|
||||
|
||||
*buf = append(*buf, '"')
|
||||
return nil
|
||||
}
|
||||
|
|
@ -156,7 +195,14 @@ func (self *Node) encodeArray(buf *[]byte) error {
|
|||
|
||||
func (self *Pair) encode(buf *[]byte) error {
|
||||
*buf = append(*buf, '"')
|
||||
*buf = append(*buf, self.Key...)
|
||||
sptr := (*rt.GoString)(unsafe.Pointer(&self.Key))
|
||||
if sptr.Len == 0 {
|
||||
*buf = append(*buf, '"', ':')
|
||||
return self.Value.encode(buf)
|
||||
}
|
||||
|
||||
quote(buf, sptr.Ptr, sptr.Len)
|
||||
|
||||
*buf = append(*buf, '"', ':')
|
||||
return self.Value.encode(buf)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -72,6 +72,10 @@ func TestEncodeValue(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
quote, err := encoder.Encode(_TwitterJson, 0)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
type Case struct {
|
||||
node Node
|
||||
exp string
|
||||
|
|
@ -83,9 +87,12 @@ func TestEncodeValue(t *testing.T) {
|
|||
{NewBool(false), "false", false},
|
||||
{NewNumber("0.0"), "0.0", false},
|
||||
{NewString(""), `""`, false},
|
||||
{NewString("\"\""), `"\"\""`, false},
|
||||
{NewString(_TwitterJson), string(quote), false},
|
||||
{NewArray([]Node{}), "[]", false},
|
||||
{NewArray([]Node{NewBool(true), NewString("true")}), `[true,"true"]`, false},
|
||||
{NewArray([]Node{NewBool(true), NewString("true"), NewString("\t")}), `[true,"true","\t"]`, false},
|
||||
{NewObject([]Pair{Pair{"a", NewNull()}, Pair{"b", NewNumber("0")}}), `{"a":null,"b":0}`, false},
|
||||
{NewObject([]Pair{Pair{"\ta", NewString("\t")}, Pair{"\bb", NewString("\b")}, Pair{"\nb", NewString("\n")}, Pair{"\ra", NewString("\r")}}), `{"\ta":"\t","\bb":"\b","\nb":"\n","\ra":"\r"}`, false},
|
||||
{NewObject([]Pair{}), `{}`, false},
|
||||
{NewBytes([]byte("hello, world")), `"aGVsbG8sIHdvcmxk"`, false},
|
||||
{NewAny(obj), string(buf), false},
|
||||
|
|
@ -145,6 +152,10 @@ func BenchmarkEncodeRaw(b *testing.B) {
|
|||
if e != nil {
|
||||
b.Fatal(root)
|
||||
}
|
||||
_, err := root.MarshalJSON()
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
b.SetBytes(int64(len(data)))
|
||||
b.ResetTimer()
|
||||
for i:=0; i<b.N; i++ {
|
||||
|
|
@ -161,6 +172,11 @@ func BenchmarkEncodeSkip(b *testing.B) {
|
|||
if e != 0 {
|
||||
b.Fatal(root)
|
||||
}
|
||||
root.skipAllKey()
|
||||
_, err := root.MarshalJSON()
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
b.SetBytes(int64(len(data)))
|
||||
b.ResetTimer()
|
||||
for i:=0; i<b.N; i++ {
|
||||
|
|
@ -178,6 +194,10 @@ func BenchmarkEncodeLoad(b *testing.B) {
|
|||
b.Fatal(root)
|
||||
}
|
||||
root.loadAllKey()
|
||||
_, err := root.MarshalJSON()
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
b.SetBytes(int64(len(data)))
|
||||
b.ResetTimer()
|
||||
for i:=0; i<b.N; i++ {
|
||||
|
|
|
|||
Loading…
Reference in a new issue