Add noescape API for encoder

This commit is contained in:
Masaaki Goshima 2020-12-30 19:32:38 +09:00
parent af6922cd2d
commit b0b330a2dd
4 changed files with 96 additions and 22 deletions

View File

@ -9,24 +9,20 @@ jobs:
go-version: [ "1.13", "1.14", "1.15" ]
runs-on: ${{ matrix.os }}
steps:
- name: Set up Go ${{ matrix.go-version }}
- name: setup Go ${{ matrix.go-version }}
uses: actions/setup-go@v2
with:
go-version: ${{ matrix.go-version }}
- name: Check out code into the Go module directory
- name: checkout
uses: actions/checkout@v2
- name: Test
- name: simple test
run: go test -v ./ -count=1
# - name: Test with GC
# run: go test -v ./ -count=1
# env:
# GOGC: 1
# - name: Test with race detector
# run: go test -v -race ./ -count=1
- name: test with GC pressure
run: go test -v ./ -count=1
env:
GOGC: 1
- name: test with race detector
run: go test -v -race ./ -count=1
coverage:
name: Coverage
runs-on: ubuntu-latest

View File

@ -56,6 +56,15 @@ func Benchmark_Encode_SmallStruct_GoJson(b *testing.B) {
}
}
func Benchmark_Encode_SmallStruct_GoJsonNoEscape(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
if _, err := gojson.MarshalNoEscape(NewSmallPayload()); err != nil {
b.Fatal(err)
}
}
}
func Benchmark_Encode_SmallStructCached_EncodingJson(b *testing.B) {
cached := NewSmallPayload()
b.ReportAllocs()
@ -107,6 +116,16 @@ func Benchmark_Encode_SmallStructCached_GoJson(b *testing.B) {
}
}
func Benchmark_Encode_SmallStructCached_GoJsonNoEscape(b *testing.B) {
cached := NewSmallPayload()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
if _, err := gojson.MarshalNoEscape(cached); err != nil {
b.Fatal(err)
}
}
}
func Benchmark_Encode_MediumStruct_EncodingJson(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
@ -153,6 +172,15 @@ func Benchmark_Encode_MediumStruct_GoJson(b *testing.B) {
}
}
func Benchmark_Encode_MediumStruct_GoJsonNoEscape(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
if _, err := gojson.MarshalNoEscape(NewMediumPayload()); err != nil {
b.Fatal(err)
}
}
}
func Benchmark_Encode_MediumStructCached_EncodingJson(b *testing.B) {
cached := NewMediumPayload()
b.ReportAllocs()
@ -204,6 +232,16 @@ func Benchmark_Encode_MediumStructCached_GoJson(b *testing.B) {
}
}
func Benchmark_Encode_MediumStructCached_GoJsonNoEscape(b *testing.B) {
cached := NewMediumPayload()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
if _, err := gojson.MarshalNoEscape(cached); err != nil {
b.Fatal(err)
}
}
}
func Benchmark_Encode_LargeStruct_EncodingJson(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
@ -250,6 +288,15 @@ func Benchmark_Encode_LargeStruct_GoJson(b *testing.B) {
}
}
func Benchmark_Encode_LargeStruct_GoJsonNoEscape(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
if _, err := gojson.MarshalNoEscape(NewLargePayload()); err != nil {
b.Fatal(err)
}
}
}
func Benchmark_Encode_LargeStructCached_EncodingJson(b *testing.B) {
cached := NewLargePayload()
b.ReportAllocs()
@ -300,3 +347,13 @@ func Benchmark_Encode_LargeStructCached_GoJson(b *testing.B) {
}
}
}
func Benchmark_Encode_LargeStructCached_GoJsonNoEscape(b *testing.B) {
cached := NewLargePayload()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
if _, err := gojson.MarshalNoEscape(cached); err != nil {
b.Fatal(err)
}
}
}

View File

@ -111,7 +111,8 @@ func (e *Encoder) EncodeWithOption(v interface{}, opts ...EncodeOption) error {
return err
}
}
buf, err := e.encode(v)
header := (*interfaceHeader)(unsafe.Pointer(&v))
buf, err := e.encode(header)
if err != nil {
return err
}
@ -159,8 +160,8 @@ func (e *Encoder) reset() {
e.unorderedMap = false
}
func (e *Encoder) encodeForMarshal(v interface{}) ([]byte, error) {
buf, err := e.encode(v)
func (e *Encoder) encodeForMarshal(header *interfaceHeader) ([]byte, error) {
buf, err := e.encode(header)
if err != nil {
return nil, err
}
@ -177,9 +178,9 @@ func (e *Encoder) encodeForMarshal(v interface{}) ([]byte, error) {
return copied, nil
}
func (e *Encoder) encode(v interface{}) ([]byte, error) {
func (e *Encoder) encode(header *interfaceHeader) ([]byte, error) {
b := e.buf[:0]
if v == nil {
if header.ptr == nil {
b = encodeNull(b)
if e.enabledIndent {
b = encodeIndentComma(b)
@ -188,7 +189,6 @@ func (e *Encoder) encode(v interface{}) ([]byte, error) {
}
return b, nil
}
header := (*interfaceHeader)(unsafe.Pointer(&v))
typ := header.typ
typeptr := uintptr(unsafe.Pointer(typ))

27
json.go
View File

@ -4,6 +4,7 @@ import (
"bytes"
"errors"
"strconv"
"unsafe"
)
// Marshaler is the interface implemented by types that
@ -157,6 +158,20 @@ func Marshal(v interface{}) ([]byte, error) {
return MarshalWithOption(v)
}
// MarshalNoEscape
func MarshalNoEscape(v interface{}) ([]byte, error) {
var b *bytes.Buffer
enc := NewEncoder(b)
header := (*interfaceHeader)(unsafe.Pointer(&v))
bytes, err := enc.encodeForMarshal(header)
if err != nil {
enc.release()
return nil, err
}
enc.release()
return bytes, nil
}
// MarshalWithOption returns the JSON encoding of v with EncodeOption.
func MarshalWithOption(v interface{}, opts ...EncodeOption) ([]byte, error) {
var b *bytes.Buffer
@ -166,7 +181,9 @@ func MarshalWithOption(v interface{}, opts ...EncodeOption) ([]byte, error) {
return nil, err
}
}
bytes, err := enc.encodeForMarshal(v)
header := (*interfaceHeader)(unsafe.Pointer(&v))
enc.ptr = header.ptr
bytes, err := enc.encodeForMarshal(header)
if err != nil {
enc.release()
return nil, err
@ -192,7 +209,9 @@ func MarshalIndentWithOption(v interface{}, prefix, indent string, opts ...Encod
}
}
enc.SetIndent(prefix, indent)
bytes, err := enc.encodeForMarshal(v)
header := (*interfaceHeader)(unsafe.Pointer(&v))
enc.ptr = header.ptr
bytes, err := enc.encodeForMarshal(header)
if err != nil {
enc.release()
return nil, err
@ -393,7 +412,9 @@ func HTMLEscape(dst *bytes.Buffer, src []byte) {
}
enc := NewEncoder(dst)
enc.SetEscapeHTML(true)
enc.buf, _ = enc.encode(v)
header := (*interfaceHeader)(unsafe.Pointer(&v))
enc.ptr = header.ptr
enc.buf, _ = enc.encode(header)
dst.Write(enc.buf[:len(enc.buf)-1]) // remove last ',' character
}