Merge branch 'master' into patch/typespec-docs

This commit is contained in:
GoWebProd
2020-03-30 23:27:08 +03:00
committed by GitHub
38 changed files with 6228 additions and 8576 deletions
+1
View File
@@ -3,3 +3,4 @@
*.iml
.idea
*.swp
bin/*
+6 -3
View File
@@ -2,8 +2,11 @@ language: go
go:
- tip
- stable
matrix:
allow_failures:
- go: tip
install:
- go get github.com/ugorji/go/codec
- go get github.com/pquerna/ffjson/fflib/v1
- go get github.com/json-iterator/go
- go get golang.org/x/lint/golint
+42 -49
View File
@@ -1,65 +1,58 @@
PKG=github.com/mailru/easyjson
GOPATH:=$(PWD)/.root:$(GOPATH)
export GOPATH
all: test
.root/src/$(PKG):
mkdir -p $@
for i in $$PWD/* ; do ln -s $$i $@/`basename $$i` ; done
root: .root/src/$(PKG)
clean:
rm -rf .root
rm -rf bin
rm -rf tests/*_easyjson.go
rm -rf benchmark/*_easyjson.go
build:
go build -i -o .root/bin/easyjson $(PKG)/easyjson
go build -i -o ./bin/easyjson ./easyjson
generate: root build
.root/bin/easyjson -stubs \
.root/src/$(PKG)/tests/snake.go \
.root/src/$(PKG)/tests/data.go \
.root/src/$(PKG)/tests/omitempty.go \
.root/src/$(PKG)/tests/nothing.go \
.root/src/$(PKG)/tests/named_type.go \
.root/src/$(PKG)/tests/custom_map_key_type.go \
.root/src/$(PKG)/tests/embedded_type.go \
.root/src/$(PKG)/tests/reference_to_pointer.go \
.root/src/$(PKG)/tests/type_declaration.go
generate: build
bin/easyjson -stubs \
./tests/snake.go \
./tests/data.go \
./tests/omitempty.go \
./tests/nothing.go \
./tests/named_type.go \
./tests/custom_map_key_type.go \
./tests/embedded_type.go \
./tests/reference_to_pointer.go \
./tests/html.go \
./tests/unknown_fields.go \
./tests/type_declaration.go
.root/bin/easyjson -all .root/src/$(PKG)/tests/data.go
.root/bin/easyjson -all .root/src/$(PKG)/tests/nothing.go
.root/bin/easyjson -all .root/src/$(PKG)/tests/errors.go
.root/bin/easyjson -snake_case .root/src/$(PKG)/tests/snake.go
.root/bin/easyjson -omit_empty .root/src/$(PKG)/tests/omitempty.go
.root/bin/easyjson -build_tags=use_easyjson .root/src/$(PKG)/benchmark/data.go
.root/bin/easyjson .root/src/$(PKG)/tests/nested_easy.go
.root/bin/easyjson .root/src/$(PKG)/tests/named_type.go
.root/bin/easyjson .root/src/$(PKG)/tests/custom_map_key_type.go
.root/bin/easyjson .root/src/$(PKG)/tests/embedded_type.go
.root/bin/easyjson .root/src/$(PKG)/tests/reference_to_pointer.go
.root/bin/easyjson -disallow_unknown_fields .root/src/$(PKG)/tests/disallow_unknown.go
.root/bin/easyjson .root/src/$(PKG)/tests/type_declaration.go
bin/easyjson -all ./tests/data.go
bin/easyjson -all ./tests/nothing.go
bin/easyjson -all ./tests/errors.go
bin/easyjson -all ./tests/html.go
bin/easyjson -snake_case ./tests/snake.go
bin/easyjson -omit_empty ./tests/omitempty.go
bin/easyjson -build_tags=use_easyjson ./benchmark/data.go
bin/easyjson ./tests/nested_easy.go
bin/easyjson ./tests/named_type.go
bin/easyjson ./tests/custom_map_key_type.go
bin/easyjson ./tests/embedded_type.go
bin/easyjson ./tests/reference_to_pointer.go
bin/easyjson ./tests/key_marshaler_map.go
bin/easyjson -disallow_unknown_fields ./tests/disallow_unknown.go
bin/easyjson ./tests/unknown_fields.go
bin/easyjson ./tests/type_declaration.go
test: generate root
test: generate
go test \
$(PKG)/tests \
$(PKG)/jlexer \
$(PKG)/gen \
$(PKG)/buffer
go test -benchmem -tags use_easyjson -bench . $(PKG)/benchmark
golint -set_exit_status .root/src/$(PKG)/tests/*_easyjson.go
./tests \
./jlexer \
./gen \
./buffer
cd benchmark && go test -benchmem -tags use_easyjson -bench .
golint -set_exit_status ./tests/*_easyjson.go
bench-other: generate root
@go test -benchmem -bench . $(PKG)/benchmark
@go test -benchmem -tags use_ffjson -bench . $(PKG)/benchmark
@go test -benchmem -tags use_jsoniter -bench . $(PKG)/benchmark
@go test -benchmem -tags use_codec -bench . $(PKG)/benchmark
bench-other: generate
cd benchmark && make
bench-python:
benchmark/ujson.sh
.PHONY: root clean generate test build
.PHONY: clean generate test build
+8 -5
View File
@@ -79,7 +79,7 @@ Additional option notes:
## Generated Marshaler/Unmarshaler Funcs
For Go struct types, easyjson generates the funcs `MarshalEasyJSON` /
`UnmarshalEasyJSON` for marshaling/unmarshaling JSON. In turn, these satisify
`UnmarshalEasyJSON` for marshaling/unmarshaling JSON. In turn, these satisfy
the `easyjson.Marshaler` and `easyjson.Unmarshaler` interfaces and when used in
conjunction with `easyjson.Marshal` / `easyjson.Unmarshal` avoid unnecessary
reflection / type assertions during marshaling/unmarshaling to/from JSON for Go
@@ -102,17 +102,17 @@ utility funcs that are available.
## Controlling easyjson Marshaling and Unmarshaling Behavior
Go types can provide their own `MarshalEasyJSON` and `UnmarshalEasyJSON` funcs
that satisify the `easyjson.Marshaler` / `easyjson.Unmarshaler` interfaces.
that satisfy the `easyjson.Marshaler` / `easyjson.Unmarshaler` interfaces.
These will be used by `easyjson.Marshal` and `easyjson.Unmarshal` when defined
for a Go type.
Go types can also satisify the `easyjson.Optional` interface, which allows the
Go types can also satisfy the `easyjson.Optional` interface, which allows the
type to define its own `omitempty` logic.
## Type Wrappers
easyjson provides additional type wrappers defined in the `easyjson/opt`
package. These wrap the standard Go primitives and in turn satisify the
package. These wrap the standard Go primitives and in turn satisfy the
easyjson interfaces.
The `easyjson/opt` type wrappers are useful when needing to distinguish between
@@ -173,6 +173,9 @@ for more information.
typically for many uses/protocols the final, marshaled length of the JSON
needs to be known prior to sending the data. Currently this is not possible
with easyjson's architecture.
* easyjson parser and codegen based on reflection, so it won't work on `package main`
files, because they cant be imported by parser.
## Benchmarks
@@ -236,7 +239,7 @@ since the memory is not freed between marshaling operations.
### easyjson vs 'ujson' python module
[ujson](https://github.com/esnme/ultrajson) is using C code for parsing, so it
is interesting to see how plain golang compares to that. It is imporant to note
is interesting to see how plain golang compares to that. It is important to note
that the resulting object for python is slower to access, since the library
parses JSON object into dictionaries.
+5
View File
@@ -0,0 +1,5 @@
all:
go test -benchmem -bench .
go test -benchmem -tags use_ffjson -bench .
go test -benchmem -tags use_jsoniter -bench .
go test -benchmem -tags use_codec -bench .
+3499 -6391
View File
File diff suppressed because it is too large Load Diff
+2071 -2001
View File
File diff suppressed because it is too large Load Diff
+14
View File
@@ -0,0 +1,14 @@
module github.com/mailru/easyjson/benchmark
go 1.12
require (
github.com/json-iterator/go v1.1.7
github.com/mailru/easyjson v0.0.0
github.com/pquerna/ffjson v0.0.0-20190813045741-dac163c6c0a9
github.com/ugorji/go/codec v1.1.7
github.com/ugorji/go/codec/codecgen v1.1.7
golang.org/x/tools v0.0.0-20190829051458-42f498d34c4d // indirect
)
replace github.com/mailru/easyjson => ../
+31
View File
@@ -0,0 +1,31 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/json-iterator/go v1.1.7 h1:KfgG9LzI+pYjr4xvmz/5H4FXjokeP+rlHLhv3iH62Fo=
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pquerna/ffjson v0.0.0-20190813045741-dac163c6c0a9 h1:kyf9snWXHvQc+yxE9imhdI8YAm4oKeZISlaAR+x73zs=
github.com/pquerna/ffjson v0.0.0-20190813045741-dac163c6c0a9/go.mod h1:YARuvh7BUWHNhzDq2OM5tzR2RiCcN2D7sapiKyCel/M=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
github.com/ugorji/go/codec/codecgen v1.1.7 h1:6BU4y9NIgvVMNetSGxkH9lOOa2UB5b8sCHC6+8m5lVc=
github.com/ugorji/go/codec/codecgen v1.1.7/go.mod h1:FMgcDIjFRtjQPUVM3GN592ic8epl2qsvfNPhezMgP8Y=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20190829051458-42f498d34c4d h1:yqT69RdmShXXRtsT9jS6Iy0FFLWGLCe3IqGE0vsP0m4=
golang.org/x/tools v0.0.0-20190829051458-42f498d34c4d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+9
View File
@@ -0,0 +1,9 @@
//+build tools
// Package tools tracks dependencies on binaries not otherwise referenced in the codebase.
// https://github.com/golang/go/wiki/Modules#how-can-i-track-tool-dependencies-for-a-module
package tools
import (
_ "github.com/ugorji/go/codec/codecgen"
)
+14 -10
View File
@@ -7,6 +7,7 @@ package bootstrap
import (
"fmt"
"go/format"
"io/ioutil"
"os"
"os/exec"
@@ -176,18 +177,21 @@ func (g *Generator) Run() error {
if err = cmd.Run(); err != nil {
return err
}
f.Close()
if !g.NoFormat {
cmd = exec.Command("gofmt", "-w", f.Name())
cmd.Stderr = os.Stderr
cmd.Stdout = os.Stdout
if err = cmd.Run(); err != nil {
return err
}
// move unformatted file to out path
if g.NoFormat {
return os.Rename(f.Name(), g.OutName)
}
return os.Rename(f.Name(), g.OutName)
// format file and write to out path
in, err := ioutil.ReadFile(f.Name())
if err != nil {
return err
}
out, err := format.Source(in)
if err != nil {
return err
}
return ioutil.WriteFile(g.OutName, out, 0644)
}
+40 -32
View File
@@ -4,6 +4,7 @@ package buffer
import (
"io"
"net"
"sync"
)
@@ -52,14 +53,12 @@ func putBuf(buf []byte) {
// getBuf gets a chunk from reuse pool or creates a new one if reuse failed.
func getBuf(size int) []byte {
if size < config.PooledSize {
return make([]byte, 0, size)
}
if c := buffers[size]; c != nil {
v := c.Get()
if v != nil {
return v.([]byte)
if size >= config.PooledSize {
if c := buffers[size]; c != nil {
v := c.Get()
if v != nil {
return v.([]byte)
}
}
}
return make([]byte, 0, size)
@@ -78,9 +77,12 @@ type Buffer struct {
// EnsureSpace makes sure that the current chunk contains at least s free bytes,
// possibly creating a new chunk.
func (b *Buffer) EnsureSpace(s int) {
if cap(b.Buf)-len(b.Buf) >= s {
return
if cap(b.Buf)-len(b.Buf) < s {
b.ensureSpaceSlow(s)
}
}
func (b *Buffer) ensureSpaceSlow(s int) {
l := len(b.Buf)
if l > 0 {
if cap(b.toPool) != cap(b.Buf) {
@@ -105,18 +107,22 @@ func (b *Buffer) EnsureSpace(s int) {
// AppendByte appends a single byte to buffer.
func (b *Buffer) AppendByte(data byte) {
if cap(b.Buf) == len(b.Buf) { // EnsureSpace won't be inlined.
b.EnsureSpace(1)
}
b.EnsureSpace(1)
b.Buf = append(b.Buf, data)
}
// AppendBytes appends a byte slice to buffer.
func (b *Buffer) AppendBytes(data []byte) {
if len(data) <= cap(b.Buf)-len(b.Buf) {
b.Buf = append(b.Buf, data...) // fast path
} else {
b.appendBytesSlow(data)
}
}
func (b *Buffer) appendBytesSlow(data []byte) {
for len(data) > 0 {
if cap(b.Buf) == len(b.Buf) { // EnsureSpace won't be inlined.
b.EnsureSpace(1)
}
b.EnsureSpace(1)
sz := cap(b.Buf) - len(b.Buf)
if sz > len(data) {
@@ -128,12 +134,18 @@ func (b *Buffer) AppendBytes(data []byte) {
}
}
// AppendBytes appends a string to buffer.
// AppendString appends a string to buffer.
func (b *Buffer) AppendString(data string) {
if len(data) <= cap(b.Buf)-len(b.Buf) {
b.Buf = append(b.Buf, data...) // fast path
} else {
b.appendStringSlow(data)
}
}
func (b *Buffer) appendStringSlow(data string) {
for len(data) > 0 {
if cap(b.Buf) == len(b.Buf) { // EnsureSpace won't be inlined.
b.EnsureSpace(1)
}
b.EnsureSpace(1)
sz := cap(b.Buf) - len(b.Buf)
if sz > len(data) {
@@ -156,18 +168,14 @@ func (b *Buffer) Size() int {
// DumpTo outputs the contents of a buffer to a writer and resets the buffer.
func (b *Buffer) DumpTo(w io.Writer) (written int, err error) {
var n int
for _, buf := range b.bufs {
if err == nil {
n, err = w.Write(buf)
written += n
}
putBuf(buf)
bufs := net.Buffers(b.bufs)
if len(b.Buf) > 0 {
bufs = append(bufs, b.Buf)
}
n, err := bufs.WriteTo(w)
if err == nil {
n, err = w.Write(b.Buf)
written += n
for _, buf := range b.bufs {
putBuf(buf)
}
putBuf(b.toPool)
@@ -175,7 +183,7 @@ func (b *Buffer) DumpTo(w io.Writer) (written int, err error) {
b.Buf = nil
b.toPool = nil
return
return int(n), err
}
// BuildBytes creates a single byte slice with all the contents of the buffer. Data is
@@ -192,7 +200,7 @@ func (b *Buffer) BuildBytes(reuse ...[]byte) []byte {
var ret []byte
size := b.Size()
// If we got a buffer as argument and it is big enought, reuse it.
// If we got a buffer as argument and it is big enough, reuse it.
if len(reuse) == 1 && cap(reuse[0]) >= size {
ret = reuse[0][:0]
} else {
+1 -1
View File
@@ -42,7 +42,7 @@ func TestAppendString(t *testing.T) {
s := "test"
for i := 0; i < 1000; i++ {
b.AppendBytes([]byte(s))
b.AppendString(s)
want = append(want, s...)
}
+31 -8
View File
@@ -94,6 +94,16 @@ func hasCustomUnmarshaler(t reflect.Type) bool {
t.Implements(reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem())
}
func hasUnknownsUnmarshaler(t reflect.Type) bool {
t = reflect.PtrTo(t)
return t.Implements(reflect.TypeOf((*easyjson.UnknownsUnmarshaler)(nil)).Elem())
}
func hasUnknownsMarshaler(t reflect.Type) bool {
t = reflect.PtrTo(t)
return t.Implements(reflect.TypeOf((*easyjson.UnknownsMarshaler)(nil)).Elem())
}
// genTypeDecoderNoCheck generates decoding code for the type t.
func (g *Generator) genTypeDecoderNoCheck(t reflect.Type, out string, tags fieldTags, indent int) error {
ws := strings.Repeat(" ", indent)
@@ -124,9 +134,9 @@ func (g *Generator) genTypeDecoderNoCheck(t reflect.Type, out string, tags field
} else {
capacity := minSliceBytes / elem.Size()
if capacity == 0 {
capacity = 1
capacity := 1
if elem.Size() > 0 {
capacity = minSliceBytes / int(elem.Size())
}
fmt.Fprintln(g.out, ws+"if in.IsNull() {")
@@ -228,19 +238,30 @@ func (g *Generator) genTypeDecoderNoCheck(t reflect.Type, out string, tags field
} // else assume the caller knows what they are doing and that the custom unmarshaler performs the translation from string or integer keys to the key type
elem := t.Elem()
tmpVar := g.uniqueVarName()
keepEmpty := tags.required || tags.noOmitEmpty || (!g.omitEmpty && !tags.omitEmpty)
fmt.Fprintln(g.out, ws+"if in.IsNull() {")
fmt.Fprintln(g.out, ws+" in.Skip()")
fmt.Fprintln(g.out, ws+"} else {")
fmt.Fprintln(g.out, ws+" in.Delim('{')")
fmt.Fprintln(g.out, ws+" if !in.IsDelim('}') {")
if !keepEmpty {
fmt.Fprintln(g.out, ws+" if !in.IsDelim('}') {")
}
fmt.Fprintln(g.out, ws+" "+out+" = make("+g.getType(t)+")")
fmt.Fprintln(g.out, ws+" } else {")
fmt.Fprintln(g.out, ws+" "+out+" = nil")
fmt.Fprintln(g.out, ws+" }")
if !keepEmpty {
fmt.Fprintln(g.out, ws+" } else {")
fmt.Fprintln(g.out, ws+" "+out+" = nil")
fmt.Fprintln(g.out, ws+" }")
}
fmt.Fprintln(g.out, ws+" for !in.IsDelim('}') {")
if keyDec != "" {
// NOTE: extra check for TextUnmarshaler. It overrides default methods.
if reflect.PtrTo(key).Implements(reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem()) {
fmt.Fprintln(g.out, ws+" var key "+g.getType(key))
fmt.Fprintln(g.out, ws+"if data := in.UnsafeBytes(); in.Ok() {")
fmt.Fprintln(g.out, ws+" in.AddError(key.UnmarshalText(data) )")
fmt.Fprintln(g.out, ws+"}")
} else if keyDec != "" {
fmt.Fprintln(g.out, ws+" key := "+g.getType(key)+"("+keyDec+")")
} else {
fmt.Fprintln(g.out, ws+" var key "+g.getType(key))
@@ -474,6 +495,8 @@ func (g *Generator) genStructDecoder(t reflect.Type) error {
Reason: "unknown field",
Data: key,
})`)
} else if hasUnknownsUnmarshaler(t) {
fmt.Fprintln(g.out, " out.UnmarshalUnknown(in, key)")
} else {
fmt.Fprintln(g.out, " in.SkipRecursive()")
}
+51 -15
View File
@@ -126,7 +126,9 @@ func (g *Generator) genTypeEncoderNoCheck(t reflect.Type, in string, tags fieldT
if enc := primitiveStringEncoders[t.Kind()]; enc != "" && tags.asString {
fmt.Fprintf(g.out, ws+enc+"\n", in)
return nil
} else if enc := primitiveEncoders[t.Kind()]; enc != "" {
}
if enc := primitiveEncoders[t.Kind()]; enc != "" {
fmt.Fprintf(g.out, ws+enc+"\n", in)
return nil
}
@@ -223,7 +225,11 @@ func (g *Generator) genTypeEncoderNoCheck(t reflect.Type, in string, tags fieldT
fmt.Fprintln(g.out, ws+" "+tmpVar+"First := true")
fmt.Fprintln(g.out, ws+" for "+tmpVar+"Name, "+tmpVar+"Value := range "+in+" {")
fmt.Fprintln(g.out, ws+" if "+tmpVar+"First { "+tmpVar+"First = false } else { out.RawByte(',') }")
if keyEnc != "" {
// NOTE: extra check for TextMarshaler. It overrides default methods.
if reflect.PtrTo(key).Implements(reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem()) {
fmt.Fprintln(g.out, ws+" "+fmt.Sprintf("out.RawText(("+tmpVar+"Name).MarshalText()"+")"))
} else if keyEnc != "" {
fmt.Fprintln(g.out, ws+" "+fmt.Sprintf(keyEnc, tmpVar+"Name"))
} else {
if err := g.genTypeEncoder(key, tmpVar+"Name", tags, indent+2, false); err != nil {
@@ -286,32 +292,50 @@ func (g *Generator) notEmptyCheck(t reflect.Type, v string) string {
}
}
func (g *Generator) genStructFieldEncoder(t reflect.Type, f reflect.StructField) error {
func (g *Generator) genStructFieldEncoder(t reflect.Type, f reflect.StructField, first, firstCondition bool) (bool, error) {
jsonName := g.fieldNamer.GetJSONFieldName(t, f)
tags := parseFieldTags(f)
if tags.omit {
return nil
return firstCondition, nil
}
toggleFirstCondition := firstCondition
noOmitEmpty := (!tags.omitEmpty && !g.omitEmpty) || tags.noOmitEmpty
if noOmitEmpty {
fmt.Fprintln(g.out, " {")
toggleFirstCondition = false
} else {
fmt.Fprintln(g.out, " if", g.notEmptyCheck(f.Type, "in."+f.Name), "{")
// can be any in runtime, so toggleFirstCondition stay as is
}
if firstCondition {
fmt.Fprintf(g.out, " const prefix string = %q\n", ","+strconv.Quote(jsonName)+":")
if first {
if !noOmitEmpty {
fmt.Fprintln(g.out, " first = false")
}
fmt.Fprintln(g.out, " out.RawString(prefix[1:])")
} else {
fmt.Fprintln(g.out, " if first {")
fmt.Fprintln(g.out, " first = false")
fmt.Fprintln(g.out, " out.RawString(prefix[1:])")
fmt.Fprintln(g.out, " } else {")
fmt.Fprintln(g.out, " out.RawString(prefix)")
fmt.Fprintln(g.out, " }")
}
} else {
fmt.Fprintf(g.out, " const prefix string = %q\n", ","+strconv.Quote(jsonName)+":")
fmt.Fprintln(g.out, " out.RawString(prefix)")
}
fmt.Fprintf(g.out, " const prefix string = %q\n", ","+strconv.Quote(jsonName)+":")
fmt.Fprintln(g.out, " if first {")
fmt.Fprintln(g.out, " first = false")
fmt.Fprintln(g.out, " out.RawString(prefix[1:])")
fmt.Fprintln(g.out, " } else {")
fmt.Fprintln(g.out, " out.RawString(prefix)")
fmt.Fprintln(g.out, " }")
if err := g.genTypeEncoder(f.Type, "in."+f.Name, tags, 2, !noOmitEmpty); err != nil {
return err
return toggleFirstCondition, err
}
fmt.Fprintln(g.out, " }")
return nil
return toggleFirstCondition, nil
}
func (g *Generator) genEncoder(t reflect.Type) error {
@@ -359,12 +383,24 @@ func (g *Generator) genStructEncoder(t reflect.Type) error {
if err != nil {
return fmt.Errorf("cannot generate encoder for %v: %v", t, err)
}
for _, f := range fs {
if err := g.genStructFieldEncoder(t, f); err != nil {
firstCondition := true
for i, f := range fs {
firstCondition, err = g.genStructFieldEncoder(t, f, i == 0, firstCondition)
if err != nil {
return err
}
}
if hasUnknownsMarshaler(t) {
if !firstCondition {
fmt.Fprintln(g.out, " in.MarshalUnknowns(out, false)")
} else {
fmt.Fprintln(g.out, " in.MarshalUnknowns(out, first)")
}
}
fmt.Fprintln(g.out, " out.RawByte('}')")
fmt.Fprintln(g.out, "}")
+7 -6
View File
@@ -156,8 +156,9 @@ func (g *Generator) printHeader() {
fmt.Println("package ", g.pkgName)
fmt.Println()
byAlias := map[string]string{}
var aliases []string
byAlias := make(map[string]string, len(g.imports))
aliases := make([]string, 0, len(g.imports))
for path, alias := range g.imports {
aliases = append(aliases, alias)
byAlias[alias] = path
@@ -388,9 +389,9 @@ func (DefaultFieldNamer) GetJSONFieldName(t reflect.Type, f reflect.StructField)
jsonName := strings.Split(f.Tag.Get("json"), ",")[0]
if jsonName != "" {
return jsonName
} else {
return f.Name
}
return f.Name
}
// LowerCamelCaseFieldNamer
@@ -454,9 +455,9 @@ func (LowerCamelCaseFieldNamer) GetJSONFieldName(t reflect.Type, f reflect.Struc
jsonName := strings.Split(f.Tag.Get("json"), ",")[0]
if jsonName != "" {
return jsonName
} else {
return lowerFirst(f.Name)
}
return lowerFirst(f.Name)
}
// SnakeCaseFieldNamer implements CamelCase to snake_case conversion for fields names.
+3
View File
@@ -0,0 +1,3 @@
module github.com/mailru/easyjson
go 1.12
+10
View File
@@ -26,6 +26,16 @@ type Optional interface {
IsDefined() bool
}
// UnknownsUnmarshaler provides a method to unmarshal unknown struct fileds and save them as you want
type UnknownsUnmarshaler interface {
UnmarshalUnknown(in *jlexer.Lexer, key string)
}
// UnknownsMarshaler provides a method to write additional struct fields
type UnknownsMarshaler interface {
MarshalUnknowns(w *jwriter.Writer, first bool)
}
// Marshal returns data as a single byte slice. Method is suboptimal as the data is likely to be copied
// from a chain of smaller chunks.
func Marshal(v Marshaler) ([]byte, error) {
+1 -1
View File
@@ -1152,7 +1152,7 @@ func (r *Lexer) Interface() interface{} {
} else if r.token.delimValue == '[' {
r.consume()
var ret []interface{}
ret := []interface{}{}
for !r.IsDelim(']') {
ret = append(ret, r.Interface())
r.WantComma()
+1 -1
View File
@@ -194,7 +194,7 @@ func TestInterface(t *testing.T) {
{toParse: "5", want: float64(5)},
{toParse: `{}`, want: map[string]interface{}{}},
{toParse: `[]`, want: []interface{}(nil)},
{toParse: `[]`, want: []interface{}{}},
{toParse: `{"a": "b"}`, want: map[string]interface{}{"a": "b"}},
{toParse: `[5]`, want: []interface{}{float64(5)}},

Some files were not shown because too many files have changed in this diff Show More