From 9a01c9afdbb32d90050fa430a2bc235c3d2783e4 Mon Sep 17 00:00:00 2001 From: Kirill Korotaev Date: Tue, 4 Feb 2020 18:16:09 +0300 Subject: [PATCH] Add new -disable_members_unescape option to avoid unescaping of member names, ints, ... Improves performance for about 15% on BenchmarkEJ_Unmarshal_M-8 --- Makefile | 2 +- bootstrap/bootstrap.go | 20 ++++++++++++-------- easyjson/main.go | 30 ++++++++++++++++-------------- gen/decoder.go | 3 +++ gen/generator.go | 16 +++++++++++----- jlexer/lexer.go | 9 ++++++--- 6 files changed, 49 insertions(+), 31 deletions(-) diff --git a/Makefile b/Makefile index ce30363..dbe6a2b 100644 --- a/Makefile +++ b/Makefile @@ -28,7 +28,7 @@ generate: build 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 -build_tags=use_easyjson -disable_members_unescape ./benchmark/data.go bin/easyjson ./tests/nested_easy.go bin/easyjson ./tests/named_type.go bin/easyjson ./tests/custom_map_key_type.go diff --git a/bootstrap/bootstrap.go b/bootstrap/bootstrap.go index 9d1c2e3..7e984d9 100644 --- a/bootstrap/bootstrap.go +++ b/bootstrap/bootstrap.go @@ -23,18 +23,19 @@ type Generator struct { PkgPath, PkgName string Types []string - NoStdMarshalers bool - SnakeCase bool - LowerCamelCase bool - OmitEmpty bool - DisallowUnknownFields bool + NoStdMarshalers bool + SnakeCase bool + LowerCamelCase bool + OmitEmpty bool + DisallowUnknownFields bool + SkipMemberNameUnescaping bool OutName string BuildTags string - StubsOnly bool - LeaveTemps bool - NoFormat bool + StubsOnly bool + LeaveTemps bool + NoFormat bool SimpleBytes bool } @@ -129,6 +130,9 @@ func (g *Generator) writeMain() (path string, err error) { if g.SimpleBytes { fmt.Fprintln(f, " g.SimpleBytes()") } + if g.SkipMemberNameUnescaping { + fmt.Fprintln(f, " g.SkipMemberNameUnescaping()") + } sort.Strings(g.Types) for _, v := range g.Types { diff --git a/easyjson/main.go b/easyjson/main.go index 26e95cb..7e935fb 100644 --- a/easyjson/main.go +++ b/easyjson/main.go @@ -29,6 +29,7 @@ var noformat = flag.Bool("noformat", false, "do not run 'gofmt -w' on output fil var specifiedName = flag.String("output_filename", "", "specify the filename of the output") var processPkg = flag.Bool("pkg", false, "process the whole package instead of just the given file") var disallowUnknownFields = flag.Bool("disallow_unknown_fields", false, "return error if any unknown field in json appeared") +var skipMemberNameUnescaping = flag.Bool("disable_members_unescape", false, "don't perform unescaping of member names to improve performance") func generate(fname string) (err error) { fInfo, err := os.Stat(fname) @@ -62,20 +63,21 @@ func generate(fname string) (err error) { } g := bootstrap.Generator{ - BuildTags: trimmedBuildTags, - PkgPath: p.PkgPath, - PkgName: p.PkgName, - Types: p.StructNames, - SnakeCase: *snakeCase, - LowerCamelCase: *lowerCamelCase, - NoStdMarshalers: *noStdMarshalers, - DisallowUnknownFields: *disallowUnknownFields, - OmitEmpty: *omitEmpty, - LeaveTemps: *leaveTemps, - OutName: outName, - StubsOnly: *stubs, - NoFormat: *noformat, - SimpleBytes: *simpleBytes, + BuildTags: trimmedBuildTags, + PkgPath: p.PkgPath, + PkgName: p.PkgName, + Types: p.StructNames, + SnakeCase: *snakeCase, + LowerCamelCase: *lowerCamelCase, + NoStdMarshalers: *noStdMarshalers, + DisallowUnknownFields: *disallowUnknownFields, + SkipMemberNameUnescaping: *skipMemberNameUnescaping, + OmitEmpty: *omitEmpty, + LeaveTemps: *leaveTemps, + OutName: outName, + StubsOnly: *stubs, + NoFormat: *noformat, + SimpleBytes: *simpleBytes, } if err := g.Run(); err != nil { diff --git a/gen/decoder.go b/gen/decoder.go index 2a17cc7..5fa6179 100644 --- a/gen/decoder.go +++ b/gen/decoder.go @@ -457,6 +457,9 @@ func (g *Generator) genStructDecoder(t reflect.Type) error { typ := g.getType(t) fmt.Fprintln(g.out, "func "+fname+"(in *jlexer.Lexer, out *"+typ+") {") + if g.skipMemberNameUnescaping { + fmt.Fprintln(g.out, " in.SkipUnescape = true") + } fmt.Fprintln(g.out, " isTopLevel := in.IsStart()") fmt.Fprintln(g.out, " if in.IsNull() {") fmt.Fprintln(g.out, " if isTopLevel {") diff --git a/gen/generator.go b/gen/generator.go index 344e516..79f4d6f 100644 --- a/gen/generator.go +++ b/gen/generator.go @@ -33,11 +33,12 @@ type Generator struct { varCounter int - noStdMarshalers bool - omitEmpty bool - disallowUnknownFields bool - fieldNamer FieldNamer - simpleBytes bool + noStdMarshalers bool + omitEmpty bool + disallowUnknownFields bool + fieldNamer FieldNamer + simpleBytes bool + skipMemberNameUnescaping bool // package path to local alias map for tracking imports imports map[string]string @@ -117,6 +118,11 @@ func (g *Generator) DisallowUnknownFields() { g.disallowUnknownFields = true } +// SkipMemberNameUnescaping instructs to skip member names unescaping to improve performance +func (g *Generator) SkipMemberNameUnescaping() { + g.skipMemberNameUnescaping = true +} + // OmitEmpty triggers `json=",omitempty"` behaviour by default. func (g *Generator) OmitEmpty() { g.omitEmpty = true diff --git a/jlexer/lexer.go b/jlexer/lexer.go index 0254862..53ff2a8 100644 --- a/jlexer/lexer.go +++ b/jlexer/lexer.go @@ -49,6 +49,7 @@ type Lexer struct { firstElement bool // Whether current element is the first in array or an object. wantSep byte // A comma or a colon character, which need to occur before a token. + SkipUnescape bool // Skip unescaping on all but user returned strings (i.e. member names, numbers, bytes etc.) UseMultipleErrors bool // If we want to use multiple errors. fatalError error // Fatal error occurred during lexing. It is usually a syntax error. @@ -606,9 +607,11 @@ func (r *Lexer) unsafeString() (string, []byte) { r.errInvalidToken("string") return "", nil } - if err := r.unescapeStringToken(); err != nil { - r.errInvalidToken("string") - return "", nil + if !r.SkipUnescape { + if err := r.unescapeStringToken(); err != nil { + r.errInvalidToken("string") + return "", nil + } } bytes := r.token.byteValue