Merge pull request #79 from goccy/feature/add-noescape-api

Add noescape API for encoder
This commit is contained in:
Masaaki Goshima 2020-12-30 19:44:47 +09:00 committed by GitHub
commit 4c60205184
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 96 additions and 22 deletions

View File

@ -9,24 +9,20 @@ jobs:
go-version: [ "1.13", "1.14", "1.15" ] go-version: [ "1.13", "1.14", "1.15" ]
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:
- name: Set up Go ${{ matrix.go-version }} - name: setup Go ${{ matrix.go-version }}
uses: actions/setup-go@v2 uses: actions/setup-go@v2
with: with:
go-version: ${{ matrix.go-version }} go-version: ${{ matrix.go-version }}
- name: checkout
- name: Check out code into the Go module directory
uses: actions/checkout@v2 uses: actions/checkout@v2
- name: simple test
- name: Test
run: go test -v ./ -count=1 run: go test -v ./ -count=1
- name: test with GC pressure
# - name: Test with GC run: go test -v ./ -count=1
# run: go test -v ./ -count=1 env:
# env: GOGC: 1
# GOGC: 1 - name: test with race detector
run: go test -v -race ./ -count=1
# - name: Test with race detector
# run: go test -v -race ./ -count=1
coverage: coverage:
name: Coverage name: Coverage
runs-on: ubuntu-latest 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) { func Benchmark_Encode_SmallStructCached_EncodingJson(b *testing.B) {
cached := NewSmallPayload() cached := NewSmallPayload()
b.ReportAllocs() 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) { func Benchmark_Encode_MediumStruct_EncodingJson(b *testing.B) {
b.ReportAllocs() b.ReportAllocs()
for i := 0; i < b.N; i++ { 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) { func Benchmark_Encode_MediumStructCached_EncodingJson(b *testing.B) {
cached := NewMediumPayload() cached := NewMediumPayload()
b.ReportAllocs() 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) { func Benchmark_Encode_LargeStruct_EncodingJson(b *testing.B) {
b.ReportAllocs() b.ReportAllocs()
for i := 0; i < b.N; i++ { 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) { func Benchmark_Encode_LargeStructCached_EncodingJson(b *testing.B) {
cached := NewLargePayload() cached := NewLargePayload()
b.ReportAllocs() 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 return err
} }
} }
buf, err := e.encode(v) header := (*interfaceHeader)(unsafe.Pointer(&v))
buf, err := e.encode(header, v == nil)
if err != nil { if err != nil {
return err return err
} }
@ -159,8 +160,8 @@ func (e *Encoder) reset() {
e.unorderedMap = false e.unorderedMap = false
} }
func (e *Encoder) encodeForMarshal(v interface{}) ([]byte, error) { func (e *Encoder) encodeForMarshal(header *interfaceHeader, isNil bool) ([]byte, error) {
buf, err := e.encode(v) buf, err := e.encode(header, isNil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -177,9 +178,9 @@ func (e *Encoder) encodeForMarshal(v interface{}) ([]byte, error) {
return copied, nil return copied, nil
} }
func (e *Encoder) encode(v interface{}) ([]byte, error) { func (e *Encoder) encode(header *interfaceHeader, isNil bool) ([]byte, error) {
b := e.buf[:0] b := e.buf[:0]
if v == nil { if isNil {
b = encodeNull(b) b = encodeNull(b)
if e.enabledIndent { if e.enabledIndent {
b = encodeIndentComma(b) b = encodeIndentComma(b)
@ -188,7 +189,6 @@ func (e *Encoder) encode(v interface{}) ([]byte, error) {
} }
return b, nil return b, nil
} }
header := (*interfaceHeader)(unsafe.Pointer(&v))
typ := header.typ typ := header.typ
typeptr := uintptr(unsafe.Pointer(typ)) typeptr := uintptr(unsafe.Pointer(typ))

27
json.go
View File

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