├── tests
├── nothing.go
├── html.go
├── members_escaped.go
├── members_unescaped.go
├── nocopy.go
├── disallow_unknown.go
├── escaping.go
├── reference_to_pointer.go
├── snake.go
├── intern.go
├── type_declaration_skip.go
├── omitempty.go
├── text_marshaler.go
├── unknown_fields.go
├── nested_marshaler.go
├── key_marshaler_map.go
├── named_type.go
├── nested_easy.go
├── embedded_type.go
├── html_test.go
├── custom_map_key_type.go
├── errors.go
├── type_declaration.go
├── intern_test.go
├── text_marshaler_test.go
├── members_escaping_test.go
├── unknown_fields_test.go
├── opt_test.go
├── required_test.go
├── escaping_test.go
├── nocopy_test.go
├── errors_test.go
└── basic_test.go
├── .gitignore
├── parser
├── testdata
│ ├── default.go.mod
│ ├── missing_module.go.mod
│ ├── comments.go.mod
│ └── comments_deps.go.mod
├── pkgpath_test.go
├── modulepath.go
├── parser.go
└── pkgpath.go
├── go.mod
├── go.sum
├── benchmark
├── Makefile
├── dummy_test.go
├── tools.go
├── ujson.sh
├── go.mod
├── default_test.go
├── jsoniter_test.go
├── go.sum
├── easyjson_test.go
├── ffjson_test.go
├── codec_test.go
├── data.go
├── data_var.go
└── example.json
├── helpers_test.go
├── jlexer
├── error.go
├── bytestostr_nounsafe.go
├── bytestostr.go
└── lexer_test.go
├── unknown_fields.go
├── LICENSE
├── raw.go
├── opt
├── opts.go
├── gotemplate_Int.go
├── gotemplate_Bool.go
├── gotemplate_Int8.go
├── gotemplate_Uint.go
├── gotemplate_Int16.go
├── gotemplate_Int32.go
├── gotemplate_Int64.go
├── gotemplate_Uint8.go
├── gotemplate_String.go
├── gotemplate_Uint16.go
├── gotemplate_Uint32.go
├── gotemplate_Uint64.go
├── optional
│ └── opt.go
├── gotemplate_Float32.go
└── gotemplate_Float64.go
├── .github
└── workflows
│ └── easyjson.yml
├── Makefile
├── buffer
├── pool_test.go
└── pool.go
├── gen
└── generator_test.go
├── helpers.go
├── easyjson
└── main.go
├── bootstrap
└── bootstrap.go
└── jwriter
└── writer.go
/tests/nothing.go:
--------------------------------------------------------------------------------
1 | package tests
2 |
3 | // No structs in this file
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .root
2 | *_easyjson.go
3 | *.iml
4 | .idea
5 | *.swp
6 | bin/*
7 |
--------------------------------------------------------------------------------
/parser/testdata/default.go.mod:
--------------------------------------------------------------------------------
1 | module example.com/user/project
2 |
3 | go 1.13
4 |
--------------------------------------------------------------------------------
/tests/html.go:
--------------------------------------------------------------------------------
1 | package tests
2 |
3 | type Struct struct {
4 | Test string
5 | }
6 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/mailru/easyjson
2 |
3 | go 1.20
4 |
5 | require github.com/josharian/intern v1.0.0
6 |
--------------------------------------------------------------------------------
/parser/testdata/missing_module.go.mod:
--------------------------------------------------------------------------------
1 |
2 | go 1.13
3 |
4 | require (
5 | github.com/mailru/easyjson v0.7.0
6 | )
7 |
--------------------------------------------------------------------------------
/tests/members_escaped.go:
--------------------------------------------------------------------------------
1 | package tests
2 |
3 | //easyjson:json
4 | type MembersEscaped struct {
5 | A string `json:"漢語"`
6 | }
7 |
--------------------------------------------------------------------------------
/tests/members_unescaped.go:
--------------------------------------------------------------------------------
1 | package tests
2 |
3 | //easyjson:json
4 | type MembersUnescaped struct {
5 | A string `json:"漢語"`
6 | }
7 |
--------------------------------------------------------------------------------
/tests/nocopy.go:
--------------------------------------------------------------------------------
1 | package tests
2 |
3 | //easyjson:json
4 | type NocopyStruct struct {
5 | A string `json:"a"`
6 | B string `json:"b,nocopy"`
7 | }
8 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
2 | github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
3 |
--------------------------------------------------------------------------------
/parser/testdata/comments.go.mod:
--------------------------------------------------------------------------------
1 | // first-line comment which should bresk anything
2 | module example.com/user/project // end-line comment which should not break anything
3 |
4 | go 1.13
5 |
--------------------------------------------------------------------------------
/benchmark/Makefile:
--------------------------------------------------------------------------------
1 | all:
2 | go test -benchmem -bench .
3 | go test -benchmem -tags use_ffjson -bench .
4 | go test -benchmem -tags use_jsoniter -bench .
5 | go test -benchmem -tags use_codec -bench .
6 |
--------------------------------------------------------------------------------
/tests/disallow_unknown.go:
--------------------------------------------------------------------------------
1 | package tests
2 |
3 | //easyjson:json
4 | type DisallowUnknown struct {
5 | FieldOne string `json:"field_one"`
6 | }
7 |
8 | var disallowUnknownString = `{"field_one": "one", "field_two": "two"}`
9 |
--------------------------------------------------------------------------------
/tests/escaping.go:
--------------------------------------------------------------------------------
1 | package tests
2 |
3 | //easyjson:json
4 | type EscStringStruct struct {
5 | A string `json:"a"`
6 | }
7 |
8 | //easyjson:json
9 | type EscIntStruct struct {
10 | A int `json:"a,string"`
11 | }
12 |
--------------------------------------------------------------------------------
/tests/reference_to_pointer.go:
--------------------------------------------------------------------------------
1 | package tests
2 |
3 | type Struct1 struct {
4 | }
5 |
6 | //easyjson:json
7 | type Struct2 struct {
8 | From *Struct1 `json:"from,omitempty"`
9 | Through *Struct1 `json:"through,omitempty"`
10 | }
11 |
--------------------------------------------------------------------------------
/helpers_test.go:
--------------------------------------------------------------------------------
1 | package easyjson
2 |
3 | import "testing"
4 |
5 | func BenchmarkNilCheck(b *testing.B) {
6 | var a *int
7 | for i := 0; i < b.N; i++ {
8 | if !isNilInterface(a) {
9 | b.Fatal("expected it to be nil")
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/parser/testdata/comments_deps.go.mod:
--------------------------------------------------------------------------------
1 | // first-line comment which should bresk anything
2 | module example.com/user/project // end-line comment which should not break anything
3 |
4 | go 1.13
5 |
6 | require (
7 | github.com/mailru/easyjson v0.7.0
8 | )
9 |
--------------------------------------------------------------------------------
/benchmark/dummy_test.go:
--------------------------------------------------------------------------------
1 | package benchmark
2 |
3 | import (
4 | "testing"
5 | )
6 |
7 | type DummyWriter struct{}
8 |
9 | func (w DummyWriter) Write(data []byte) (int, error) { return len(data), nil }
10 |
11 | func TestToSuppressNoTestsWarning(t *testing.T) {}
12 |
--------------------------------------------------------------------------------
/tests/snake.go:
--------------------------------------------------------------------------------
1 | package tests
2 |
3 | //easyjson:json
4 | type SnakeStruct struct {
5 | WeirdHTTPStuff bool
6 | CustomNamedField string `json:"cUsToM"`
7 | }
8 |
9 | var snakeStructValue SnakeStruct
10 | var snakeStructString = `{"weird_http_stuff":false,"cUsToM":""}`
11 |
--------------------------------------------------------------------------------
/benchmark/tools.go:
--------------------------------------------------------------------------------
1 | //+build tools
2 |
3 | // Package tools tracks dependencies on binaries not otherwise referenced in the codebase.
4 | // https://github.com/golang/go/wiki/Modules#how-can-i-track-tool-dependencies-for-a-module
5 | package tools
6 |
7 | import (
8 | _ "github.com/ugorji/go/codec/codecgen"
9 | )
10 |
--------------------------------------------------------------------------------
/tests/intern.go:
--------------------------------------------------------------------------------
1 | package tests
2 |
3 | //easyjson:json
4 | type NoIntern struct {
5 | Field string `json:"field"`
6 | }
7 |
8 | //easyjson:json
9 | type Intern struct {
10 | Field string `json:"field,intern"`
11 | }
12 |
13 | var intern = Intern{Field: "interned"}
14 | var internString = `{"field":"interned"}`
15 |
--------------------------------------------------------------------------------
/tests/type_declaration_skip.go:
--------------------------------------------------------------------------------
1 | package tests
2 |
3 | //easyjson:skip
4 | type TypeSkipped struct {
5 | Value string
6 | }
7 |
8 | type TypeNotSkipped struct {
9 | Value string
10 | }
11 |
12 | var (
13 | myTypeNotSkippedValue = TypeDeclared{Value: "TypeNotSkipped"}
14 | myTypeNotSkippedString = `{"Value":"TypeNotSkipped"}`
15 | )
16 |
--------------------------------------------------------------------------------
/tests/omitempty.go:
--------------------------------------------------------------------------------
1 | package tests
2 |
3 | //easyjson:json
4 | type OmitEmptyDefault struct {
5 | Field string
6 | Str string
7 | Str1 string `json:"s,!omitempty"`
8 | Str2 string `json:",!omitempty"`
9 | }
10 |
11 | var omitEmptyDefaultValue = OmitEmptyDefault{Field: "test"}
12 | var omitEmptyDefaultString = `{"Field":"test","s":"","Str2":""}`
13 |
--------------------------------------------------------------------------------
/tests/text_marshaler.go:
--------------------------------------------------------------------------------
1 | package tests
2 |
3 | import (
4 | "strings"
5 | )
6 |
7 | //easyjson:json
8 | type StructWrappedTextMarshaler struct {
9 | Value namedWithTextMarshaler
10 | }
11 | type namedWithTextMarshaler string
12 |
13 | func (n namedWithTextMarshaler) MarshalText() ([]byte, error) {
14 | return []byte(strings.ToUpper(string(n))), nil
15 | }
16 |
--------------------------------------------------------------------------------
/benchmark/ujson.sh:
--------------------------------------------------------------------------------
1 | #/bin/bash
2 |
3 | echo -n "Python ujson module, DECODE: "
4 | python -m timeit -s "import ujson; data = open('`dirname $0`/example.json', 'r').read()" 'ujson.loads(data)'
5 |
6 | echo -n "Python ujson module, ENCODE: "
7 | python -m timeit -s "import ujson; data = open('`dirname $0`/example.json', 'r').read(); obj = ujson.loads(data)" 'ujson.dumps(obj)'
8 |
--------------------------------------------------------------------------------
/tests/unknown_fields.go:
--------------------------------------------------------------------------------
1 | package tests
2 |
3 | import "github.com/mailru/easyjson"
4 |
5 | //easyjson:json
6 | type StructWithUnknownsProxy struct {
7 | easyjson.UnknownFieldsProxy
8 |
9 | Field1 string
10 | }
11 |
12 | //easyjson:json
13 | type StructWithUnknownsProxyWithOmitempty struct {
14 | easyjson.UnknownFieldsProxy
15 |
16 | Field1 string `json:",omitempty"`
17 | }
18 |
--------------------------------------------------------------------------------
/jlexer/error.go:
--------------------------------------------------------------------------------
1 | package jlexer
2 |
3 | import "fmt"
4 |
5 | // LexerError implements the error interface and represents all possible errors that can be
6 | // generated during parsing the JSON data.
7 | type LexerError struct {
8 | Reason string
9 | Offset int
10 | Data string
11 | }
12 |
13 | func (l *LexerError) Error() string {
14 | return fmt.Sprintf("parse error: %s near offset %d of '%s'", l.Reason, l.Offset, l.Data)
15 | }
16 |
--------------------------------------------------------------------------------
/jlexer/bytestostr_nounsafe.go:
--------------------------------------------------------------------------------
1 | // This file is included to the build if any of the buildtags below
2 | // are defined. Refer to README notes for more details.
3 |
4 | //+build easyjson_nounsafe appengine
5 |
6 | package jlexer
7 |
8 | // bytesToStr creates a string normally from []byte
9 | //
10 | // Note that this method is roughly 1.5x slower than using the 'unsafe' method.
11 | func bytesToStr(data []byte) string {
12 | return string(data)
13 | }
14 |
--------------------------------------------------------------------------------
/benchmark/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/mailru/easyjson/benchmark
2 |
3 | go 1.12
4 |
5 | require (
6 | github.com/json-iterator/go v1.1.7
7 | github.com/mailru/easyjson v0.0.0
8 | github.com/pquerna/ffjson v0.0.0-20190813045741-dac163c6c0a9
9 | github.com/ugorji/go/codec v1.1.7
10 | github.com/ugorji/go/codec/codecgen v1.1.7
11 | golang.org/x/tools v0.0.0-20190829051458-42f498d34c4d // indirect
12 | )
13 |
14 | replace github.com/mailru/easyjson => ../
15 |
--------------------------------------------------------------------------------
/tests/nested_marshaler.go:
--------------------------------------------------------------------------------
1 | package tests
2 |
3 | import (
4 | "github.com/mailru/easyjson"
5 | "github.com/mailru/easyjson/jlexer"
6 | "github.com/mailru/easyjson/jwriter"
7 | )
8 |
9 | //easyjson:json
10 | type NestedMarshaler struct {
11 | Value easyjson.MarshalerUnmarshaler
12 | Value2 int
13 | }
14 |
15 | type StructWithMarshaler struct {
16 | Value int
17 | }
18 |
19 | func (s *StructWithMarshaler) UnmarshalEasyJSON(w *jlexer.Lexer) {
20 | s.Value = w.Int()
21 | }
22 |
23 | func (s *StructWithMarshaler) MarshalEasyJSON(w *jwriter.Writer) {
24 | w.Int(s.Value)
25 | }
26 |
27 |
--------------------------------------------------------------------------------
/tests/key_marshaler_map.go:
--------------------------------------------------------------------------------
1 | package tests
2 |
3 | type KeyWithEncodingMarshaler int
4 |
5 | func (f KeyWithEncodingMarshaler) MarshalText() (text []byte, err error) {
6 | return []byte("hello"), nil
7 | }
8 |
9 | func (f *KeyWithEncodingMarshaler) UnmarshalText(text []byte) error {
10 | if string(text) == "hello" {
11 | *f = 5
12 | }
13 | return nil
14 | }
15 |
16 | //easyjson:json
17 | type KeyWithEncodingMarshalers map[KeyWithEncodingMarshaler]string
18 |
19 | var mapWithEncodingMarshaler KeyWithEncodingMarshalers = KeyWithEncodingMarshalers{5: "hello"}
20 | var mapWithEncodingMarshalerString = `{"hello":"hello"}`
21 |
--------------------------------------------------------------------------------
/tests/named_type.go:
--------------------------------------------------------------------------------
1 | package tests
2 |
3 | //easyjson:json
4 | type NamedType struct {
5 | Inner struct {
6 | // easyjson is mistakenly naming the type of this field 'tests.MyString' in the generated output
7 | // something about a named type inside an anonmymous type is triggering this bug
8 | Field MyString `tag:"value"`
9 | Field2 int "tag:\"value with ` in it\""
10 | }
11 | }
12 |
13 | type MyString string
14 |
15 | var namedTypeValue NamedType
16 |
17 | func init() {
18 | namedTypeValue.Inner.Field = "test"
19 | namedTypeValue.Inner.Field2 = 123
20 | }
21 |
22 | var namedTypeValueString = `{"Inner":{"Field":"test","Field2":123}}`
23 |
--------------------------------------------------------------------------------
/tests/nested_easy.go:
--------------------------------------------------------------------------------
1 | package tests
2 |
3 | import (
4 | "github.com/mailru/easyjson"
5 | "github.com/mailru/easyjson/jwriter"
6 | )
7 |
8 | //easyjson:json
9 | type NestedInterfaces struct {
10 | Value interface{}
11 | Slice []interface{}
12 | Map map[string]interface{}
13 | }
14 |
15 | type NestedEasyMarshaler struct {
16 | EasilyMarshaled bool
17 | }
18 |
19 | var _ easyjson.Marshaler = &NestedEasyMarshaler{}
20 |
21 | func (i *NestedEasyMarshaler) MarshalEasyJSON(w *jwriter.Writer) {
22 | // We use this method only to indicate that easyjson.Marshaler
23 | // interface was really used while encoding.
24 | i.EasilyMarshaled = true
25 | }
26 |
--------------------------------------------------------------------------------
/tests/embedded_type.go:
--------------------------------------------------------------------------------
1 | package tests
2 |
3 | //easyjson:json
4 | type EmbeddedType struct {
5 | EmbeddedInnerType
6 | Inner struct {
7 | EmbeddedInnerType
8 | }
9 | Field2 int
10 | EmbeddedInnerType2 `json:"named"`
11 | }
12 |
13 | type EmbeddedInnerType struct {
14 | Field1 int
15 | }
16 |
17 | type EmbeddedInnerType2 struct {
18 | Field3 int
19 | }
20 |
21 | var embeddedTypeValue EmbeddedType
22 |
23 | func init() {
24 | embeddedTypeValue.Field1 = 1
25 | embeddedTypeValue.Field2 = 2
26 | embeddedTypeValue.Inner.Field1 = 3
27 | embeddedTypeValue.Field3 = 4
28 | }
29 |
30 | var embeddedTypeValueString = `{"Inner":{"Field1":3},"Field2":2,"named":{"Field3":4},"Field1":1}`
31 |
--------------------------------------------------------------------------------
/tests/html_test.go:
--------------------------------------------------------------------------------
1 | package tests
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/mailru/easyjson/jwriter"
7 | )
8 |
9 | func TestHTML(t *testing.T) {
10 | s := Struct{
11 | Test: "test",
12 | }
13 |
14 | j := jwriter.Writer{
15 | NoEscapeHTML: false,
16 | }
17 | s.MarshalEasyJSON(&j)
18 |
19 | data, _ := j.BuildBytes()
20 |
21 | if string(data) != `{"Test":"\u003cb\u003etest\u003c/b\u003e"}` {
22 | t.Fatal("EscapeHTML error:", string(data))
23 | }
24 |
25 | j.NoEscapeHTML = true
26 | s.MarshalEasyJSON(&j)
27 |
28 | data, _ = j.BuildBytes()
29 |
30 | if string(data) != `{"Test":"test"}` {
31 | t.Fatal("NoEscapeHTML error:", string(data))
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/tests/custom_map_key_type.go:
--------------------------------------------------------------------------------
1 | package tests
2 |
3 | import fmt "fmt"
4 |
5 | //easyjson:json
6 | type CustomMapKeyType struct {
7 | Map map[customKeyType]int
8 | }
9 |
10 | type customKeyType [2]byte
11 |
12 | func (k customKeyType) MarshalJSON() ([]byte, error) {
13 | return []byte(fmt.Sprintf(`"%02x"`, k)), nil
14 | }
15 |
16 | func (k *customKeyType) UnmarshalJSON(b []byte) error {
17 | _, err := fmt.Sscanf(string(b), `"%02x%02x"`, &k[0], &k[1])
18 | return err
19 | }
20 |
21 | var customMapKeyTypeValue CustomMapKeyType
22 |
23 | func init() {
24 | customMapKeyTypeValue.Map = map[customKeyType]int{
25 | {0x01, 0x02}: 3,
26 | }
27 | }
28 |
29 | var customMapKeyTypeValueString = `{"Map":{"0102":3}}`
30 |
--------------------------------------------------------------------------------
/jlexer/bytestostr.go:
--------------------------------------------------------------------------------
1 | // This file will only be included to the build if neither
2 | // easyjson_nounsafe nor appengine build tag is set. See README notes
3 | // for more details.
4 |
5 | //+build !easyjson_nounsafe
6 | //+build !appengine
7 |
8 | package jlexer
9 |
10 | import (
11 | "unsafe"
12 | )
13 |
14 | // bytesToStr creates a string pointing at the slice to avoid copying.
15 | //
16 | // Warning: the string returned by the function should be used with care, as the whole input data
17 | // chunk may be either blocked from being freed by GC because of a single string or the buffer.Data
18 | // may be garbage-collected even when the string exists.
19 | func bytesToStr(data []byte) string {
20 | return *(*string)(unsafe.Pointer(&data))
21 | }
22 |
--------------------------------------------------------------------------------
/tests/errors.go:
--------------------------------------------------------------------------------
1 | package tests
2 |
3 | //easyjson:json
4 | type ErrorIntSlice []int
5 |
6 | //easyjson:json
7 | type ErrorBoolSlice []bool
8 |
9 | //easyjson:json
10 | type ErrorUintSlice []uint
11 |
12 | //easyjson:json
13 | type ErrorStruct struct {
14 | Int int `json:"int"`
15 | String string `json:"string"`
16 | Slice []int `json:"slice"`
17 | IntSlice []int `json:"int_slice"`
18 | }
19 |
20 | type ErrorNestedStruct struct {
21 | ErrorStruct ErrorStruct `json:"error_struct"`
22 | Int int `json:"int"`
23 | }
24 |
25 | //easyjson:json
26 | type ErrorIntMap map[uint32]string
27 |
28 | //easyjson:json
29 | type ErrorFloatTypes struct {
30 | Float64 float64 `json:"float64"`
31 | Float32 float32 `json:"float32"`
32 | }
33 |
--------------------------------------------------------------------------------
/tests/type_declaration.go:
--------------------------------------------------------------------------------
1 | package tests
2 |
3 | //easyjson:json
4 | type (
5 | GenDeclared1 struct {
6 | Value string
7 | }
8 |
9 | // A gen declared easyjson struct with a comment
10 | GenDeclaredWithComment struct {
11 | Value string
12 | }
13 | )
14 |
15 | type (
16 | //easyjson:json
17 | TypeDeclared struct {
18 | Value string
19 | }
20 |
21 | TypeNotDeclared struct {
22 | Value string
23 | }
24 | )
25 |
26 | var (
27 | myGenDeclaredValue = TypeDeclared{Value: "GenDeclared"}
28 | myGenDeclaredString = `{"Value":"GenDeclared"}`
29 | myGenDeclaredWithCommentValue = TypeDeclared{Value: "GenDeclaredWithComment"}
30 | myGenDeclaredWithCommentString = `{"Value":"GenDeclaredWithComment"}`
31 | myTypeDeclaredValue = TypeDeclared{Value: "TypeDeclared"}
32 | myTypeDeclaredString = `{"Value":"TypeDeclared"}`
33 | )
34 |
--------------------------------------------------------------------------------
/unknown_fields.go:
--------------------------------------------------------------------------------
1 | package easyjson
2 |
3 | import (
4 | jlexer "github.com/mailru/easyjson/jlexer"
5 | "github.com/mailru/easyjson/jwriter"
6 | )
7 |
8 | // UnknownFieldsProxy implemets UnknownsUnmarshaler and UnknownsMarshaler
9 | // use it as embedded field in your structure to parse and then serialize unknown struct fields
10 | type UnknownFieldsProxy struct {
11 | unknownFields map[string][]byte
12 | }
13 |
14 | func (s *UnknownFieldsProxy) UnmarshalUnknown(in *jlexer.Lexer, key string) {
15 | if s.unknownFields == nil {
16 | s.unknownFields = make(map[string][]byte, 1)
17 | }
18 | s.unknownFields[key] = in.Raw()
19 | }
20 |
21 | func (s UnknownFieldsProxy) MarshalUnknowns(out *jwriter.Writer, first bool) {
22 | for key, val := range s.unknownFields {
23 | if first {
24 | first = false
25 | } else {
26 | out.RawByte(',')
27 | }
28 | out.String(string(key))
29 | out.RawByte(':')
30 | out.Raw(val, nil)
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/tests/intern_test.go:
--------------------------------------------------------------------------------
1 | package tests
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/mailru/easyjson"
7 | )
8 |
9 | func TestStringIntern(t *testing.T) {
10 | data := []byte(`{"field": "string interning test"}`)
11 |
12 | var i Intern
13 | allocsPerRun := testing.AllocsPerRun(1000, func() {
14 | i = Intern{}
15 | err := easyjson.Unmarshal(data, &i)
16 | if err != nil {
17 | t.Error(err)
18 | }
19 | if i.Field != "string interning test" {
20 | t.Fatalf("wrong value: %q", i.Field)
21 | }
22 | })
23 | if allocsPerRun > 1 {
24 | t.Fatalf("expected <= 1 allocs, got %f", allocsPerRun)
25 | }
26 |
27 | var n NoIntern
28 | allocsPerRun = testing.AllocsPerRun(1000, func() {
29 | n = NoIntern{}
30 | err := easyjson.Unmarshal(data, &n)
31 | if err != nil {
32 | t.Error(err)
33 | }
34 | if n.Field != "string interning test" {
35 | t.Fatalf("wrong value: %q", n.Field)
36 | }
37 | })
38 | if allocsPerRun > 2 {
39 | t.Fatalf("expected <= 2 allocs, got %f", allocsPerRun)
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/tests/text_marshaler_test.go:
--------------------------------------------------------------------------------
1 | package tests
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/mailru/easyjson"
7 | )
8 |
9 | func TestStructWithTextMarshalerMarshal(t *testing.T) {
10 | tests := []struct {
11 | name string
12 | input StructWrappedTextMarshaler
13 | expected string
14 | }{
15 | {
16 | name: "Filled struct",
17 | input: StructWrappedTextMarshaler{
18 | Value: namedWithTextMarshaler("test"),
19 | },
20 | expected: `{"Value":"TEST"}`,
21 | },
22 | {
23 | name: "Empty struct",
24 | input: StructWrappedTextMarshaler{},
25 | expected: `{"Value":""}`,
26 | },
27 | }
28 |
29 | for _, test := range tests {
30 | t.Run(test.name, func(t *testing.T) {
31 | marshaled, err := easyjson.Marshal(test.input)
32 | if err != nil {
33 | t.Errorf("easyjson.Marshal failed: %v", err)
34 | }
35 | if string(marshaled) != test.expected {
36 | t.Errorf("easyjson.Marshal output incorrect: got %s, want %s", string(marshaled), test.expected)
37 | }
38 | })
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2016 Mail.Ru Group
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4 |
5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 |
7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
8 |
--------------------------------------------------------------------------------
/parser/pkgpath_test.go:
--------------------------------------------------------------------------------
1 | package parser
2 |
3 | import "testing"
4 |
5 | func Test_getModulePath(t *testing.T) {
6 | tests := map[string]struct {
7 | goModPath string
8 | want string
9 | }{
10 | "valid go.mod without comments and deps": {
11 | goModPath: "./testdata/default.go.mod",
12 | want: "example.com/user/project",
13 | },
14 | "valid go.mod with comments and without deps": {
15 | goModPath: "./testdata/comments.go.mod",
16 | want: "example.com/user/project",
17 | },
18 | "valid go.mod with comments and deps": {
19 | goModPath: "./testdata/comments_deps.go.mod",
20 | want: "example.com/user/project",
21 | },
22 | "actual easyjson go.mod": {
23 | goModPath: "../go.mod",
24 | want: "github.com/mailru/easyjson",
25 | },
26 | "invalid go.mod with missing module": {
27 | goModPath: "./testdata/missing_module.go",
28 | want: "",
29 | },
30 | }
31 | for name := range tests {
32 | tt := tests[name]
33 | t.Run(name, func(t *testing.T) {
34 | if got := getModulePath(tt.goModPath); got != tt.want {
35 | t.Errorf("getModulePath() = %v, want %v", got, tt.want)
36 | }
37 | })
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/raw.go:
--------------------------------------------------------------------------------
1 | package easyjson
2 |
3 | import (
4 | "github.com/mailru/easyjson/jlexer"
5 | "github.com/mailru/easyjson/jwriter"
6 | )
7 |
8 | // RawMessage is a raw piece of JSON (number, string, bool, object, array or
9 | // null) that is extracted without parsing and output as is during marshaling.
10 | type RawMessage []byte
11 |
12 | // MarshalEasyJSON does JSON marshaling using easyjson interface.
13 | func (v *RawMessage) MarshalEasyJSON(w *jwriter.Writer) {
14 | if len(*v) == 0 {
15 | w.RawString("null")
16 | } else {
17 | w.Raw(*v, nil)
18 | }
19 | }
20 |
21 | // UnmarshalEasyJSON does JSON unmarshaling using easyjson interface.
22 | func (v *RawMessage) UnmarshalEasyJSON(l *jlexer.Lexer) {
23 | *v = RawMessage(l.Raw())
24 | }
25 |
26 | // UnmarshalJSON implements encoding/json.Unmarshaler interface.
27 | func (v *RawMessage) UnmarshalJSON(data []byte) error {
28 | *v = make([]byte, len(data))
29 | copy(*v, data)
30 | return nil
31 | }
32 |
33 | var nullBytes = []byte("null")
34 |
35 | // MarshalJSON implements encoding/json.Marshaler interface.
36 | func (v RawMessage) MarshalJSON() ([]byte, error) {
37 | if len(v) == 0 {
38 | return nullBytes, nil
39 | }
40 | return v, nil
41 | }
42 |
43 | // IsDefined is required for integration with omitempty easyjson logic.
44 | func (v *RawMessage) IsDefined() bool {
45 | return len(*v) > 0
46 | }
47 |
--------------------------------------------------------------------------------
/opt/opts.go:
--------------------------------------------------------------------------------
1 | package opt
2 |
3 | //go:generate sed -i "s/\\+build none/generated by gotemplate/" optional/opt.go
4 | //go:generate gotemplate "github.com/mailru/easyjson/opt/optional" Int(int)
5 | //go:generate gotemplate "github.com/mailru/easyjson/opt/optional" Uint(uint)
6 |
7 | //go:generate gotemplate "github.com/mailru/easyjson/opt/optional" Int8(int8)
8 | //go:generate gotemplate "github.com/mailru/easyjson/opt/optional" Int16(int16)
9 | //go:generate gotemplate "github.com/mailru/easyjson/opt/optional" Int32(int32)
10 | //go:generate gotemplate "github.com/mailru/easyjson/opt/optional" Int64(int64)
11 |
12 | //go:generate gotemplate "github.com/mailru/easyjson/opt/optional" Uint8(uint8)
13 | //go:generate gotemplate "github.com/mailru/easyjson/opt/optional" Uint16(uint16)
14 | //go:generate gotemplate "github.com/mailru/easyjson/opt/optional" Uint32(uint32)
15 | //go:generate gotemplate "github.com/mailru/easyjson/opt/optional" Uint64(uint64)
16 |
17 | //go:generate gotemplate "github.com/mailru/easyjson/opt/optional" Float32(float32)
18 | //go:generate gotemplate "github.com/mailru/easyjson/opt/optional" Float64(float64)
19 |
20 | //go:generate gotemplate "github.com/mailru/easyjson/opt/optional" Bool(bool)
21 | //go:generate gotemplate "github.com/mailru/easyjson/opt/optional" String(string)
22 | //go:generate sed -i "s/generated by gotemplate/+build none/" optional/opt.go
23 |
--------------------------------------------------------------------------------
/tests/members_escaping_test.go:
--------------------------------------------------------------------------------
1 | package tests
2 |
3 | import (
4 | "reflect"
5 | "testing"
6 |
7 | "github.com/mailru/easyjson"
8 | )
9 |
10 | func TestMembersEscaping(t *testing.T) {
11 | cases := []struct {
12 | data string
13 | esc MembersEscaped
14 | unesc MembersUnescaped
15 | }{
16 | {
17 | data: `{"漢語": "中国"}`,
18 | esc: MembersEscaped{A: "中国"},
19 | unesc: MembersUnescaped{A: "中国"},
20 | },
21 | {
22 | data: `{"漢語": "\u4e2D\u56fD"}`,
23 | esc: MembersEscaped{A: "中国"},
24 | unesc: MembersUnescaped{A: "中国"},
25 | },
26 | {
27 | data: `{"\u6f22\u8a9E": "中国"}`,
28 | esc: MembersEscaped{A: "中国"},
29 | unesc: MembersUnescaped{A: ""},
30 | },
31 | {
32 | data: `{"\u6f22\u8a9E": "\u4e2D\u56fD"}`,
33 | esc: MembersEscaped{A: "中国"},
34 | unesc: MembersUnescaped{A: ""},
35 | },
36 | }
37 |
38 | for i, c := range cases {
39 | var esc MembersEscaped
40 | err := easyjson.Unmarshal([]byte(c.data), &esc)
41 | if err != nil {
42 | t.Error(err)
43 | }
44 | if !reflect.DeepEqual(esc, c.esc) {
45 | t.Errorf("[%d] TestMembersEscaping(): got=%+v, exp=%+v", i, esc, c.esc)
46 | }
47 |
48 | var unesc MembersUnescaped
49 | err = easyjson.Unmarshal([]byte(c.data), &unesc)
50 | if err != nil {
51 | t.Error(err)
52 | }
53 | if !reflect.DeepEqual(unesc, c.unesc) {
54 | t.Errorf("[%d] TestMembersEscaping(): no-unescaping case: got=%+v, exp=%+v", i, esc, c.esc)
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/tests/unknown_fields_test.go:
--------------------------------------------------------------------------------
1 | package tests
2 |
3 | import (
4 | "reflect"
5 | "testing"
6 | )
7 |
8 | func TestUnknownFieldsProxy(t *testing.T) {
9 | baseJson := `{"Field1":"123","Field2":"321"}`
10 |
11 | s := StructWithUnknownsProxy{}
12 |
13 | err := s.UnmarshalJSON([]byte(baseJson))
14 | if err != nil {
15 | t.Errorf("UnmarshalJSON didn't expect error: %v", err)
16 | }
17 |
18 | if s.Field1 != "123" {
19 | t.Errorf("UnmarshalJSON expected to parse Field1 as \"123\". got: %v", s.Field1)
20 | }
21 |
22 | data, err := s.MarshalJSON()
23 | if err != nil {
24 | t.Errorf("MarshalJSON didn't expect error: %v", err)
25 | }
26 |
27 | if !reflect.DeepEqual(baseJson, string(data)) {
28 | t.Errorf("MarshalJSON expected to gen: %v. got: %v", baseJson, string(data))
29 | }
30 | }
31 |
32 | func TestUnknownFieldsProxyWithOmitempty(t *testing.T) {
33 | baseJson := `{"Field1":"123","Field2":"321"}`
34 |
35 | s := StructWithUnknownsProxyWithOmitempty{}
36 |
37 | err := s.UnmarshalJSON([]byte(baseJson))
38 | if err != nil {
39 | t.Errorf("UnmarshalJSON didn't expect error: %v", err)
40 | }
41 |
42 | if s.Field1 != "123" {
43 | t.Errorf("UnmarshalJSON expected to parse Field1 as \"123\". got: %v", s.Field1)
44 | }
45 |
46 | data, err := s.MarshalJSON()
47 | if err != nil {
48 | t.Errorf("MarshalJSON didn't expect error: %v", err)
49 | }
50 |
51 | if !reflect.DeepEqual(baseJson, string(data)) {
52 | t.Errorf("MarshalJSON expected to gen: %v. got: %v", baseJson, string(data))
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/tests/opt_test.go:
--------------------------------------------------------------------------------
1 | package tests
2 |
3 | import (
4 | "math"
5 | "reflect"
6 | "testing"
7 |
8 | "encoding/json"
9 |
10 | "github.com/mailru/easyjson/opt"
11 | )
12 |
13 | // This struct type must NOT have a generated marshaler
14 | type OptsVanilla struct {
15 | Int opt.Int
16 | Uint opt.Uint
17 |
18 | Int8 opt.Int8
19 | Int16 opt.Int16
20 | Int32 opt.Int32
21 | Int64 opt.Int64
22 |
23 | Uint8 opt.Uint8
24 | Uint16 opt.Uint16
25 | Uint32 opt.Uint32
26 | Uint64 opt.Uint64
27 |
28 | Float32 opt.Float32
29 | Float64 opt.Float64
30 |
31 | Bool opt.Bool
32 | String opt.String
33 | }
34 |
35 | var optsVanillaValue = OptsVanilla{
36 | Int: opt.OInt(-123),
37 | Uint: opt.OUint(123),
38 |
39 | Int8: opt.OInt8(math.MaxInt8),
40 | Int16: opt.OInt16(math.MaxInt16),
41 | Int32: opt.OInt32(math.MaxInt32),
42 | Int64: opt.OInt64(math.MaxInt64),
43 |
44 | Uint8: opt.OUint8(math.MaxUint8),
45 | Uint16: opt.OUint16(math.MaxUint16),
46 | Uint32: opt.OUint32(math.MaxUint32),
47 | Uint64: opt.OUint64(math.MaxUint64),
48 |
49 | Float32: opt.OFloat32(math.MaxFloat32),
50 | Float64: opt.OFloat64(math.MaxFloat64),
51 |
52 | Bool: opt.OBool(true),
53 | String: opt.OString("foo"),
54 | }
55 |
56 | func TestOptsVanilla(t *testing.T) {
57 | data, err := json.Marshal(optsVanillaValue)
58 | if err != nil {
59 | t.Errorf("Failed to marshal vanilla opts: %v", err)
60 | }
61 |
62 | var ov OptsVanilla
63 | if err := json.Unmarshal(data, &ov); err != nil {
64 | t.Errorf("Failed to unmarshal vanilla opts: %v", err)
65 | }
66 |
67 | if !reflect.DeepEqual(optsVanillaValue, ov) {
68 | t.Errorf("Vanilla opts unmarshal returned invalid value %+v, want %+v", ov, optsVanillaValue)
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/tests/required_test.go:
--------------------------------------------------------------------------------
1 | package tests
2 |
3 | import (
4 | "fmt"
5 | "reflect"
6 | "testing"
7 | )
8 |
9 | func TestRequiredField(t *testing.T) {
10 | cases := []struct{ json, errorMessage string }{
11 | {`{"first_name":"Foo", "last_name": "Bar"}`, ""},
12 | {`{"last_name":"Bar"}`, "key 'first_name' is required"},
13 | {"{}", "key 'first_name' is required"},
14 | }
15 |
16 | for _, tc := range cases {
17 | var v RequiredOptionalStruct
18 | err := v.UnmarshalJSON([]byte(tc.json))
19 | if tc.errorMessage == "" {
20 | if err != nil {
21 | t.Errorf("%s. UnmarshalJSON didn`t expect error: %v", tc.json, err)
22 | }
23 | } else {
24 | if fmt.Sprintf("%v", err) != tc.errorMessage {
25 | t.Errorf("%s. UnmarshalJSON expected error: %v. got: %v", tc.json, tc.errorMessage, err)
26 | }
27 | }
28 | }
29 | }
30 |
31 | func TestRequiredOptionalMap(t *testing.T) {
32 | baseJson := `{"req_map":{}, "oe_map":{}, "noe_map":{}, "oe_slice":[]}`
33 | wantDecoding := RequiredOptionalMap{MapIntString{}, nil, MapIntString{}}
34 |
35 | var v RequiredOptionalMap
36 | if err := v.UnmarshalJSON([]byte(baseJson)); err != nil {
37 | t.Errorf("%s. UnmarshalJSON didn't expect error: %v", baseJson, err)
38 | }
39 | if !reflect.DeepEqual(v, wantDecoding) {
40 | t.Errorf("%s. UnmarshalJSON expected to gen: %v. got: %v", baseJson, wantDecoding, v)
41 | }
42 |
43 | baseStruct := RequiredOptionalMap{MapIntString{}, MapIntString{}, MapIntString{}}
44 | wantJson := `{"req_map":{},"noe_map":{}}`
45 | data, err := baseStruct.MarshalJSON()
46 | if err != nil {
47 | t.Errorf("MarshalJSON didn't expect error: %v on %v", err, data)
48 | } else if string(data) != wantJson {
49 | t.Errorf("%v. MarshalJSON wanted: %s got %s", baseStruct, wantJson, string(data))
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/tests/escaping_test.go:
--------------------------------------------------------------------------------
1 | package tests
2 |
3 | import (
4 | "reflect"
5 | "testing"
6 |
7 | "github.com/mailru/easyjson"
8 | )
9 |
10 | func TestStrFieldsUnescaping(t *testing.T) {
11 | cases := []struct {
12 | data string
13 | exp EscStringStruct
14 | }{
15 | {
16 | data: `{}`,
17 | exp: EscStringStruct{},
18 | },
19 | {
20 | data: `{"a": "\""}`,
21 | exp: EscStringStruct{A: `"`},
22 | },
23 | {
24 | data: `{"a": "\\"}`,
25 | exp: EscStringStruct{A: `\`},
26 | },
27 | {
28 | data: `{"a": "\\\""}`,
29 | exp: EscStringStruct{A: `\"`},
30 | },
31 | {
32 | data: `{"a": "\\\\'"}`,
33 | exp: EscStringStruct{A: `\\'`},
34 | },
35 | {
36 | data: `{"a": "\t\\\nx\\\""}`,
37 | exp: EscStringStruct{A: "\t\\\nx\\\""},
38 | },
39 | {
40 | data: `{"a": "\r\n"}`,
41 | exp: EscStringStruct{A: "\r\n"},
42 | },
43 | {
44 | data: `{"a": "\r\n\u4e2D\u56fD\\\""}`,
45 | exp: EscStringStruct{A: "\r\n中国\\\""},
46 | },
47 | }
48 |
49 | for i, c := range cases {
50 | var val EscStringStruct
51 | err := easyjson.Unmarshal([]byte(c.data), &val)
52 | if err != nil {
53 | t.Error(err)
54 | }
55 | if !reflect.DeepEqual(val, c.exp) {
56 | t.Errorf("[%d] TestStrFieldsUnescaping(): got=%q, exp=%q", i, val, c.exp)
57 | }
58 | }
59 | }
60 |
61 | func TestIntFieldsUnescaping(t *testing.T) {
62 | cases := []struct {
63 | data string
64 | exp EscIntStruct
65 | }{
66 | {
67 | data: `{}`,
68 | exp: EscIntStruct{A: 0},
69 | },
70 | {
71 | data: `{"a": "1"}`,
72 | exp: EscIntStruct{A: 1},
73 | },
74 | {
75 | data: `{"a": "\u0032"}`,
76 | exp: EscIntStruct{A: 2},
77 | },
78 | }
79 |
80 | for i, c := range cases {
81 | var val EscIntStruct
82 | err := easyjson.Unmarshal([]byte(c.data), &val)
83 | if err != nil {
84 | t.Error(err)
85 | }
86 | if !reflect.DeepEqual(val, c.exp) {
87 | t.Errorf("[%d] TestIntFieldsUnescaping(): got=%v, exp=%v", i, val, c.exp)
88 | }
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/tests/nocopy_test.go:
--------------------------------------------------------------------------------
1 | package tests
2 |
3 | import (
4 | "reflect"
5 | "testing"
6 | "unsafe"
7 |
8 | "github.com/mailru/easyjson"
9 | )
10 |
11 | // verifies if string pointer belongs to the given buffer or outside of it
12 | func strBelongsTo(s string, buf []byte) bool {
13 | sPtr := (*reflect.StringHeader)(unsafe.Pointer(&s)).Data
14 | bufPtr := (*reflect.SliceHeader)(unsafe.Pointer(&buf)).Data
15 |
16 | if bufPtr <= sPtr && sPtr < bufPtr+uintptr(len(buf)) {
17 | return true
18 | }
19 | return false
20 | }
21 |
22 | func TestNocopy(t *testing.T) {
23 | data := []byte(`{"a": "valueA", "b": "valueB"}`)
24 | exp := NocopyStruct{
25 | A: "valueA",
26 | B: "valueB",
27 | }
28 | res := NocopyStruct{}
29 |
30 | err := easyjson.Unmarshal(data, &res)
31 | if err != nil {
32 | t.Error(err)
33 | }
34 | if !reflect.DeepEqual(exp, res) {
35 | t.Errorf("TestNocopy(): got=%+v, exp=%+v", res, exp)
36 | }
37 |
38 | if strBelongsTo(res.A, data) {
39 | t.Error("TestNocopy(): field A was not copied and refers to buffer")
40 | }
41 | if !strBelongsTo(res.B, data) {
42 | t.Error("TestNocopy(): field B was copied rather than refer to bufferr")
43 | }
44 |
45 | data = []byte(`{"b": "valueNoCopy"}`)
46 | res = NocopyStruct{}
47 | allocsPerRun := testing.AllocsPerRun(1000, func() {
48 | err := easyjson.Unmarshal(data, &res)
49 | if err != nil {
50 | t.Error(err)
51 | }
52 | if res.B != "valueNoCopy" {
53 | t.Fatalf("wrong value: %q", res.B)
54 | }
55 | })
56 | if allocsPerRun > 1 {
57 | t.Fatalf("noCopy field unmarshal: expected <= 1 allocs, got %f", allocsPerRun)
58 | }
59 |
60 | data = []byte(`{"a": "valueNoCopy"}`)
61 | allocsPerRun = testing.AllocsPerRun(1000, func() {
62 | err := easyjson.Unmarshal(data, &res)
63 | if err != nil {
64 | t.Error(err)
65 | }
66 | if res.A != "valueNoCopy" {
67 | t.Fatalf("wrong value: %q", res.A)
68 | }
69 | })
70 | if allocsPerRun > 2 {
71 | t.Fatalf("copy field unmarshal: expected <= 2 allocs, got %f", allocsPerRun)
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/opt/gotemplate_Int.go:
--------------------------------------------------------------------------------
1 | // generated by gotemplate
2 |
3 | package opt
4 |
5 | import (
6 | "fmt"
7 |
8 | "github.com/mailru/easyjson/jlexer"
9 | "github.com/mailru/easyjson/jwriter"
10 | )
11 |
12 | // template type Optional(A)
13 |
14 | // A 'gotemplate'-based type for providing optional semantics without using pointers.
15 | type Int struct {
16 | V int
17 | Defined bool
18 | }
19 |
20 | // Creates an optional type with a given value.
21 | func OInt(v int) Int {
22 | return Int{V: v, Defined: true}
23 | }
24 |
25 | // Get returns the value or given default in the case the value is undefined.
26 | func (v Int) Get(deflt int) int {
27 | if !v.Defined {
28 | return deflt
29 | }
30 | return v.V
31 | }
32 |
33 | // MarshalEasyJSON does JSON marshaling using easyjson interface.
34 | func (v Int) MarshalEasyJSON(w *jwriter.Writer) {
35 | if v.Defined {
36 | w.Int(v.V)
37 | } else {
38 | w.RawString("null")
39 | }
40 | }
41 |
42 | // UnmarshalEasyJSON does JSON unmarshaling using easyjson interface.
43 | func (v *Int) UnmarshalEasyJSON(l *jlexer.Lexer) {
44 | if l.IsNull() {
45 | l.Skip()
46 | *v = Int{}
47 | } else {
48 | v.V = l.Int()
49 | v.Defined = true
50 | }
51 | }
52 |
53 | // MarshalJSON implements a standard json marshaler interface.
54 | func (v Int) MarshalJSON() ([]byte, error) {
55 | w := jwriter.Writer{}
56 | v.MarshalEasyJSON(&w)
57 | return w.Buffer.BuildBytes(), w.Error
58 | }
59 |
60 | // UnmarshalJSON implements a standard json unmarshaler interface.
61 | func (v *Int) UnmarshalJSON(data []byte) error {
62 | l := jlexer.Lexer{Data: data}
63 | v.UnmarshalEasyJSON(&l)
64 | return l.Error()
65 | }
66 |
67 | // IsDefined returns whether the value is defined, a function is required so that it can
68 | // be used in an interface.
69 | func (v Int) IsDefined() bool {
70 | return v.Defined
71 | }
72 |
73 | // String implements a stringer interface using fmt.Sprint for the value.
74 | func (v Int) String() string {
75 | if !v.Defined {
76 | return ""
77 | }
78 | return fmt.Sprint(v.V)
79 | }
80 |
--------------------------------------------------------------------------------
/.github/workflows/easyjson.yml:
--------------------------------------------------------------------------------
1 | name: easyjson
2 |
3 | on:
4 | push:
5 | branches: [ master ]
6 | pull_request:
7 | branches: [ master ]
8 |
9 | jobs:
10 | test:
11 | runs-on: ubuntu-latest
12 | name: Test with Go ${{ matrix.go }}
13 | strategy:
14 | fail-fast: false
15 | matrix:
16 | go: [ 1.23 ]
17 | steps:
18 | - uses: actions/checkout@v2
19 |
20 | - name: Set up Go ${{ matrix.go }}
21 | uses: actions/setup-go@v2
22 | with:
23 | go-version: ${{ matrix.go }}
24 |
25 | - name: Install golint (for old go version)
26 | if: matrix.go < 1.16
27 | run: go get golang.org/x/lint/golint && go mod tidy
28 |
29 | - name: Install golint
30 | if: matrix.go > 1.15
31 | run: go install golang.org/x/lint/golint@latest
32 |
33 | - name: Build and Run tests
34 | run: make
35 |
36 | # test-arm64:
37 | # runs-on: ubuntu-latest
38 | # name: Test on ${{ matrix.distro }} ${{ matrix.arch }}
39 | # strategy:
40 | # matrix:
41 | # include:
42 | # - arch: arm64
43 | # distro: ubuntu24.04
44 | # steps:
45 | # - uses: actions/checkout@v2
46 | # - uses: uraimo/run-on-arch-action@master
47 | # with:
48 | # arch: ${{ matrix.arch }}
49 | # distro: ${{ matrix.distro }}
50 | # install: |
51 | # apt-get update
52 | # apt install -y curl wget make gcc
53 | # latestGo=$(curl "https://golang.org/VERSION?m=text")
54 | # wget --quiet "https://dl.google.com/go/${latestGo}.linux-${{ matrix.arch }}.tar.gz"
55 | # rm -f $(which go)
56 | # rm -rf /usr/local/go
57 | # tar -C /usr/local -xzf "${latestGo}.linux-${{ matrix.arch }}.tar.gz"
58 | # run: |
59 | # export PATH=/usr/local/go/bin:$PATH
60 | # export PATH=~/go/bin:$PATH
61 | # printf "Go Version: $(go version)\n"
62 | # go install golang.org/x/lint/golint@latest
63 | # make
--------------------------------------------------------------------------------
/opt/gotemplate_Bool.go:
--------------------------------------------------------------------------------
1 | // generated by gotemplate
2 |
3 | package opt
4 |
5 | import (
6 | "fmt"
7 |
8 | "github.com/mailru/easyjson/jlexer"
9 | "github.com/mailru/easyjson/jwriter"
10 | )
11 |
12 | // template type Optional(A)
13 |
14 | // A 'gotemplate'-based type for providing optional semantics without using pointers.
15 | type Bool struct {
16 | V bool
17 | Defined bool
18 | }
19 |
20 | // Creates an optional type with a given value.
21 | func OBool(v bool) Bool {
22 | return Bool{V: v, Defined: true}
23 | }
24 |
25 | // Get returns the value or given default in the case the value is undefined.
26 | func (v Bool) Get(deflt bool) bool {
27 | if !v.Defined {
28 | return deflt
29 | }
30 | return v.V
31 | }
32 |
33 | // MarshalEasyJSON does JSON marshaling using easyjson interface.
34 | func (v Bool) MarshalEasyJSON(w *jwriter.Writer) {
35 | if v.Defined {
36 | w.Bool(v.V)
37 | } else {
38 | w.RawString("null")
39 | }
40 | }
41 |
42 | // UnmarshalEasyJSON does JSON unmarshaling using easyjson interface.
43 | func (v *Bool) UnmarshalEasyJSON(l *jlexer.Lexer) {
44 | if l.IsNull() {
45 | l.Skip()
46 | *v = Bool{}
47 | } else {
48 | v.V = l.Bool()
49 | v.Defined = true
50 | }
51 | }
52 |
53 | // MarshalJSON implements a standard json marshaler interface.
54 | func (v Bool) MarshalJSON() ([]byte, error) {
55 | w := jwriter.Writer{}
56 | v.MarshalEasyJSON(&w)
57 | return w.Buffer.BuildBytes(), w.Error
58 | }
59 |
60 | // UnmarshalJSON implements a standard json unmarshaler interface.
61 | func (v *Bool) UnmarshalJSON(data []byte) error {
62 | l := jlexer.Lexer{Data: data}
63 | v.UnmarshalEasyJSON(&l)
64 | return l.Error()
65 | }
66 |
67 | // IsDefined returns whether the value is defined, a function is required so that it can
68 | // be used in an interface.
69 | func (v Bool) IsDefined() bool {
70 | return v.Defined
71 | }
72 |
73 | // String implements a stringer interface using fmt.Sprint for the value.
74 | func (v Bool) String() string {
75 | if !v.Defined {
76 | return ""
77 | }
78 | return fmt.Sprint(v.V)
79 | }
80 |
--------------------------------------------------------------------------------
/opt/gotemplate_Int8.go:
--------------------------------------------------------------------------------
1 | // generated by gotemplate
2 |
3 | package opt
4 |
5 | import (
6 | "fmt"
7 |
8 | "github.com/mailru/easyjson/jlexer"
9 | "github.com/mailru/easyjson/jwriter"
10 | )
11 |
12 | // template type Optional(A)
13 |
14 | // A 'gotemplate'-based type for providing optional semantics without using pointers.
15 | type Int8 struct {
16 | V int8
17 | Defined bool
18 | }
19 |
20 | // Creates an optional type with a given value.
21 | func OInt8(v int8) Int8 {
22 | return Int8{V: v, Defined: true}
23 | }
24 |
25 | // Get returns the value or given default in the case the value is undefined.
26 | func (v Int8) Get(deflt int8) int8 {
27 | if !v.Defined {
28 | return deflt
29 | }
30 | return v.V
31 | }
32 |
33 | // MarshalEasyJSON does JSON marshaling using easyjson interface.
34 | func (v Int8) MarshalEasyJSON(w *jwriter.Writer) {
35 | if v.Defined {
36 | w.Int8(v.V)
37 | } else {
38 | w.RawString("null")
39 | }
40 | }
41 |
42 | // UnmarshalEasyJSON does JSON unmarshaling using easyjson interface.
43 | func (v *Int8) UnmarshalEasyJSON(l *jlexer.Lexer) {
44 | if l.IsNull() {
45 | l.Skip()
46 | *v = Int8{}
47 | } else {
48 | v.V = l.Int8()
49 | v.Defined = true
50 | }
51 | }
52 |
53 | // MarshalJSON implements a standard json marshaler interface.
54 | func (v Int8) MarshalJSON() ([]byte, error) {
55 | w := jwriter.Writer{}
56 | v.MarshalEasyJSON(&w)
57 | return w.Buffer.BuildBytes(), w.Error
58 | }
59 |
60 | // UnmarshalJSON implements a standard json unmarshaler interface.
61 | func (v *Int8) UnmarshalJSON(data []byte) error {
62 | l := jlexer.Lexer{Data: data}
63 | v.UnmarshalEasyJSON(&l)
64 | return l.Error()
65 | }
66 |
67 | // IsDefined returns whether the value is defined, a function is required so that it can
68 | // be used in an interface.
69 | func (v Int8) IsDefined() bool {
70 | return v.Defined
71 | }
72 |
73 | // String implements a stringer interface using fmt.Sprint for the value.
74 | func (v Int8) String() string {
75 | if !v.Defined {
76 | return ""
77 | }
78 | return fmt.Sprint(v.V)
79 | }
80 |
--------------------------------------------------------------------------------
/opt/gotemplate_Uint.go:
--------------------------------------------------------------------------------
1 | // generated by gotemplate
2 |
3 | package opt
4 |
5 | import (
6 | "fmt"
7 |
8 | "github.com/mailru/easyjson/jlexer"
9 | "github.com/mailru/easyjson/jwriter"
10 | )
11 |
12 | // template type Optional(A)
13 |
14 | // A 'gotemplate'-based type for providing optional semantics without using pointers.
15 | type Uint struct {
16 | V uint
17 | Defined bool
18 | }
19 |
20 | // Creates an optional type with a given value.
21 | func OUint(v uint) Uint {
22 | return Uint{V: v, Defined: true}
23 | }
24 |
25 | // Get returns the value or given default in the case the value is undefined.
26 | func (v Uint) Get(deflt uint) uint {
27 | if !v.Defined {
28 | return deflt
29 | }
30 | return v.V
31 | }
32 |
33 | // MarshalEasyJSON does JSON marshaling using easyjson interface.
34 | func (v Uint) MarshalEasyJSON(w *jwriter.Writer) {
35 | if v.Defined {
36 | w.Uint(v.V)
37 | } else {
38 | w.RawString("null")
39 | }
40 | }
41 |
42 | // UnmarshalEasyJSON does JSON unmarshaling using easyjson interface.
43 | func (v *Uint) UnmarshalEasyJSON(l *jlexer.Lexer) {
44 | if l.IsNull() {
45 | l.Skip()
46 | *v = Uint{}
47 | } else {
48 | v.V = l.Uint()
49 | v.Defined = true
50 | }
51 | }
52 |
53 | // MarshalJSON implements a standard json marshaler interface.
54 | func (v Uint) MarshalJSON() ([]byte, error) {
55 | w := jwriter.Writer{}
56 | v.MarshalEasyJSON(&w)
57 | return w.Buffer.BuildBytes(), w.Error
58 | }
59 |
60 | // UnmarshalJSON implements a standard json unmarshaler interface.
61 | func (v *Uint) UnmarshalJSON(data []byte) error {
62 | l := jlexer.Lexer{Data: data}
63 | v.UnmarshalEasyJSON(&l)
64 | return l.Error()
65 | }
66 |
67 | // IsDefined returns whether the value is defined, a function is required so that it can
68 | // be used in an interface.
69 | func (v Uint) IsDefined() bool {
70 | return v.Defined
71 | }
72 |
73 | // String implements a stringer interface using fmt.Sprint for the value.
74 | func (v Uint) String() string {
75 | if !v.Defined {
76 | return ""
77 | }
78 | return fmt.Sprint(v.V)
79 | }
80 |
--------------------------------------------------------------------------------
/opt/gotemplate_Int16.go:
--------------------------------------------------------------------------------
1 | // generated by gotemplate
2 |
3 | package opt
4 |
5 | import (
6 | "fmt"
7 |
8 | "github.com/mailru/easyjson/jlexer"
9 | "github.com/mailru/easyjson/jwriter"
10 | )
11 |
12 | // template type Optional(A)
13 |
14 | // A 'gotemplate'-based type for providing optional semantics without using pointers.
15 | type Int16 struct {
16 | V int16
17 | Defined bool
18 | }
19 |
20 | // Creates an optional type with a given value.
21 | func OInt16(v int16) Int16 {
22 | return Int16{V: v, Defined: true}
23 | }
24 |
25 | // Get returns the value or given default in the case the value is undefined.
26 | func (v Int16) Get(deflt int16) int16 {
27 | if !v.Defined {
28 | return deflt
29 | }
30 | return v.V
31 | }
32 |
33 | // MarshalEasyJSON does JSON marshaling using easyjson interface.
34 | func (v Int16) MarshalEasyJSON(w *jwriter.Writer) {
35 | if v.Defined {
36 | w.Int16(v.V)
37 | } else {
38 | w.RawString("null")
39 | }
40 | }
41 |
42 | // UnmarshalEasyJSON does JSON unmarshaling using easyjson interface.
43 | func (v *Int16) UnmarshalEasyJSON(l *jlexer.Lexer) {
44 | if l.IsNull() {
45 | l.Skip()
46 | *v = Int16{}
47 | } else {
48 | v.V = l.Int16()
49 | v.Defined = true
50 | }
51 | }
52 |
53 | // MarshalJSON implements a standard json marshaler interface.
54 | func (v Int16) MarshalJSON() ([]byte, error) {
55 | w := jwriter.Writer{}
56 | v.MarshalEasyJSON(&w)
57 | return w.Buffer.BuildBytes(), w.Error
58 | }
59 |
60 | // UnmarshalJSON implements a standard json unmarshaler interface.
61 | func (v *Int16) UnmarshalJSON(data []byte) error {
62 | l := jlexer.Lexer{Data: data}
63 | v.UnmarshalEasyJSON(&l)
64 | return l.Error()
65 | }
66 |
67 | // IsDefined returns whether the value is defined, a function is required so that it can
68 | // be used in an interface.
69 | func (v Int16) IsDefined() bool {
70 | return v.Defined
71 | }
72 |
73 | // String implements a stringer interface using fmt.Sprint for the value.
74 | func (v Int16) String() string {
75 | if !v.Defined {
76 | return ""
77 | }
78 | return fmt.Sprint(v.V)
79 | }
80 |
--------------------------------------------------------------------------------
/opt/gotemplate_Int32.go:
--------------------------------------------------------------------------------
1 | // generated by gotemplate
2 |
3 | package opt
4 |
5 | import (
6 | "fmt"
7 |
8 | "github.com/mailru/easyjson/jlexer"
9 | "github.com/mailru/easyjson/jwriter"
10 | )
11 |
12 | // template type Optional(A)
13 |
14 | // A 'gotemplate'-based type for providing optional semantics without using pointers.
15 | type Int32 struct {
16 | V int32
17 | Defined bool
18 | }
19 |
20 | // Creates an optional type with a given value.
21 | func OInt32(v int32) Int32 {
22 | return Int32{V: v, Defined: true}
23 | }
24 |
25 | // Get returns the value or given default in the case the value is undefined.
26 | func (v Int32) Get(deflt int32) int32 {
27 | if !v.Defined {
28 | return deflt
29 | }
30 | return v.V
31 | }
32 |
33 | // MarshalEasyJSON does JSON marshaling using easyjson interface.
34 | func (v Int32) MarshalEasyJSON(w *jwriter.Writer) {
35 | if v.Defined {
36 | w.Int32(v.V)
37 | } else {
38 | w.RawString("null")
39 | }
40 | }
41 |
42 | // UnmarshalEasyJSON does JSON unmarshaling using easyjson interface.
43 | func (v *Int32) UnmarshalEasyJSON(l *jlexer.Lexer) {
44 | if l.IsNull() {
45 | l.Skip()
46 | *v = Int32{}
47 | } else {
48 | v.V = l.Int32()
49 | v.Defined = true
50 | }
51 | }
52 |
53 | // MarshalJSON implements a standard json marshaler interface.
54 | func (v Int32) MarshalJSON() ([]byte, error) {
55 | w := jwriter.Writer{}
56 | v.MarshalEasyJSON(&w)
57 | return w.Buffer.BuildBytes(), w.Error
58 | }
59 |
60 | // UnmarshalJSON implements a standard json unmarshaler interface.
61 | func (v *Int32) UnmarshalJSON(data []byte) error {
62 | l := jlexer.Lexer{Data: data}
63 | v.UnmarshalEasyJSON(&l)
64 | return l.Error()
65 | }
66 |
67 | // IsDefined returns whether the value is defined, a function is required so that it can
68 | // be used in an interface.
69 | func (v Int32) IsDefined() bool {
70 | return v.Defined
71 | }
72 |
73 | // String implements a stringer interface using fmt.Sprint for the value.
74 | func (v Int32) String() string {
75 | if !v.Defined {
76 | return ""
77 | }
78 | return fmt.Sprint(v.V)
79 | }
80 |
--------------------------------------------------------------------------------
/opt/gotemplate_Int64.go:
--------------------------------------------------------------------------------
1 | // generated by gotemplate
2 |
3 | package opt
4 |
5 | import (
6 | "fmt"
7 |
8 | "github.com/mailru/easyjson/jlexer"
9 | "github.com/mailru/easyjson/jwriter"
10 | )
11 |
12 | // template type Optional(A)
13 |
14 | // A 'gotemplate'-based type for providing optional semantics without using pointers.
15 | type Int64 struct {
16 | V int64
17 | Defined bool
18 | }
19 |
20 | // Creates an optional type with a given value.
21 | func OInt64(v int64) Int64 {
22 | return Int64{V: v, Defined: true}
23 | }
24 |
25 | // Get returns the value or given default in the case the value is undefined.
26 | func (v Int64) Get(deflt int64) int64 {
27 | if !v.Defined {
28 | return deflt
29 | }
30 | return v.V
31 | }
32 |
33 | // MarshalEasyJSON does JSON marshaling using easyjson interface.
34 | func (v Int64) MarshalEasyJSON(w *jwriter.Writer) {
35 | if v.Defined {
36 | w.Int64(v.V)
37 | } else {
38 | w.RawString("null")
39 | }
40 | }
41 |
42 | // UnmarshalEasyJSON does JSON unmarshaling using easyjson interface.
43 | func (v *Int64) UnmarshalEasyJSON(l *jlexer.Lexer) {
44 | if l.IsNull() {
45 | l.Skip()
46 | *v = Int64{}
47 | } else {
48 | v.V = l.Int64()
49 | v.Defined = true
50 | }
51 | }
52 |
53 | // MarshalJSON implements a standard json marshaler interface.
54 | func (v Int64) MarshalJSON() ([]byte, error) {
55 | w := jwriter.Writer{}
56 | v.MarshalEasyJSON(&w)
57 | return w.Buffer.BuildBytes(), w.Error
58 | }
59 |
60 | // UnmarshalJSON implements a standard json unmarshaler interface.
61 | func (v *Int64) UnmarshalJSON(data []byte) error {
62 | l := jlexer.Lexer{Data: data}
63 | v.UnmarshalEasyJSON(&l)
64 | return l.Error()
65 | }
66 |
67 | // IsDefined returns whether the value is defined, a function is required so that it can
68 | // be used in an interface.
69 | func (v Int64) IsDefined() bool {
70 | return v.Defined
71 | }
72 |
73 | // String implements a stringer interface using fmt.Sprint for the value.
74 | func (v Int64) String() string {
75 | if !v.Defined {
76 | return ""
77 | }
78 | return fmt.Sprint(v.V)
79 | }
80 |
--------------------------------------------------------------------------------
/opt/gotemplate_Uint8.go:
--------------------------------------------------------------------------------
1 | // generated by gotemplate
2 |
3 | package opt
4 |
5 | import (
6 | "fmt"
7 |
8 | "github.com/mailru/easyjson/jlexer"
9 | "github.com/mailru/easyjson/jwriter"
10 | )
11 |
12 | // template type Optional(A)
13 |
14 | // A 'gotemplate'-based type for providing optional semantics without using pointers.
15 | type Uint8 struct {
16 | V uint8
17 | Defined bool
18 | }
19 |
20 | // Creates an optional type with a given value.
21 | func OUint8(v uint8) Uint8 {
22 | return Uint8{V: v, Defined: true}
23 | }
24 |
25 | // Get returns the value or given default in the case the value is undefined.
26 | func (v Uint8) Get(deflt uint8) uint8 {
27 | if !v.Defined {
28 | return deflt
29 | }
30 | return v.V
31 | }
32 |
33 | // MarshalEasyJSON does JSON marshaling using easyjson interface.
34 | func (v Uint8) MarshalEasyJSON(w *jwriter.Writer) {
35 | if v.Defined {
36 | w.Uint8(v.V)
37 | } else {
38 | w.RawString("null")
39 | }
40 | }
41 |
42 | // UnmarshalEasyJSON does JSON unmarshaling using easyjson interface.
43 | func (v *Uint8) UnmarshalEasyJSON(l *jlexer.Lexer) {
44 | if l.IsNull() {
45 | l.Skip()
46 | *v = Uint8{}
47 | } else {
48 | v.V = l.Uint8()
49 | v.Defined = true
50 | }
51 | }
52 |
53 | // MarshalJSON implements a standard json marshaler interface.
54 | func (v Uint8) MarshalJSON() ([]byte, error) {
55 | w := jwriter.Writer{}
56 | v.MarshalEasyJSON(&w)
57 | return w.Buffer.BuildBytes(), w.Error
58 | }
59 |
60 | // UnmarshalJSON implements a standard json unmarshaler interface.
61 | func (v *Uint8) UnmarshalJSON(data []byte) error {
62 | l := jlexer.Lexer{Data: data}
63 | v.UnmarshalEasyJSON(&l)
64 | return l.Error()
65 | }
66 |
67 | // IsDefined returns whether the value is defined, a function is required so that it can
68 | // be used in an interface.
69 | func (v Uint8) IsDefined() bool {
70 | return v.Defined
71 | }
72 |
73 | // String implements a stringer interface using fmt.Sprint for the value.
74 | func (v Uint8) String() string {
75 | if !v.Defined {
76 | return ""
77 | }
78 | return fmt.Sprint(v.V)
79 | }
80 |
--------------------------------------------------------------------------------
/opt/gotemplate_String.go:
--------------------------------------------------------------------------------
1 | // generated by gotemplate
2 |
3 | package opt
4 |
5 | import (
6 | "fmt"
7 |
8 | "github.com/mailru/easyjson/jlexer"
9 | "github.com/mailru/easyjson/jwriter"
10 | )
11 |
12 | // template type Optional(A)
13 |
14 | // A 'gotemplate'-based type for providing optional semantics without using pointers.
15 | type String struct {
16 | V string
17 | Defined bool
18 | }
19 |
20 | // Creates an optional type with a given value.
21 | func OString(v string) String {
22 | return String{V: v, Defined: true}
23 | }
24 |
25 | // Get returns the value or given default in the case the value is undefined.
26 | func (v String) Get(deflt string) string {
27 | if !v.Defined {
28 | return deflt
29 | }
30 | return v.V
31 | }
32 |
33 | // MarshalEasyJSON does JSON marshaling using easyjson interface.
34 | func (v String) MarshalEasyJSON(w *jwriter.Writer) {
35 | if v.Defined {
36 | w.String(v.V)
37 | } else {
38 | w.RawString("null")
39 | }
40 | }
41 |
42 | // UnmarshalEasyJSON does JSON unmarshaling using easyjson interface.
43 | func (v *String) UnmarshalEasyJSON(l *jlexer.Lexer) {
44 | if l.IsNull() {
45 | l.Skip()
46 | *v = String{}
47 | } else {
48 | v.V = l.String()
49 | v.Defined = true
50 | }
51 | }
52 |
53 | // MarshalJSON implements a standard json marshaler interface.
54 | func (v String) MarshalJSON() ([]byte, error) {
55 | w := jwriter.Writer{}
56 | v.MarshalEasyJSON(&w)
57 | return w.Buffer.BuildBytes(), w.Error
58 | }
59 |
60 | // UnmarshalJSON implements a standard json unmarshaler interface.
61 | func (v *String) UnmarshalJSON(data []byte) error {
62 | l := jlexer.Lexer{Data: data}
63 | v.UnmarshalEasyJSON(&l)
64 | return l.Error()
65 | }
66 |
67 | // IsDefined returns whether the value is defined, a function is required so that it can
68 | // be used in an interface.
69 | func (v String) IsDefined() bool {
70 | return v.Defined
71 | }
72 |
73 | // String implements a stringer interface using fmt.Sprint for the value.
74 | func (v String) String() string {
75 | if !v.Defined {
76 | return ""
77 | }
78 | return fmt.Sprint(v.V)
79 | }
80 |
--------------------------------------------------------------------------------
/opt/gotemplate_Uint16.go:
--------------------------------------------------------------------------------
1 | // generated by gotemplate
2 |
3 | package opt
4 |
5 | import (
6 | "fmt"
7 |
8 | "github.com/mailru/easyjson/jlexer"
9 | "github.com/mailru/easyjson/jwriter"
10 | )
11 |
12 | // template type Optional(A)
13 |
14 | // A 'gotemplate'-based type for providing optional semantics without using pointers.
15 | type Uint16 struct {
16 | V uint16
17 | Defined bool
18 | }
19 |
20 | // Creates an optional type with a given value.
21 | func OUint16(v uint16) Uint16 {
22 | return Uint16{V: v, Defined: true}
23 | }
24 |
25 | // Get returns the value or given default in the case the value is undefined.
26 | func (v Uint16) Get(deflt uint16) uint16 {
27 | if !v.Defined {
28 | return deflt
29 | }
30 | return v.V
31 | }
32 |
33 | // MarshalEasyJSON does JSON marshaling using easyjson interface.
34 | func (v Uint16) MarshalEasyJSON(w *jwriter.Writer) {
35 | if v.Defined {
36 | w.Uint16(v.V)
37 | } else {
38 | w.RawString("null")
39 | }
40 | }
41 |
42 | // UnmarshalEasyJSON does JSON unmarshaling using easyjson interface.
43 | func (v *Uint16) UnmarshalEasyJSON(l *jlexer.Lexer) {
44 | if l.IsNull() {
45 | l.Skip()
46 | *v = Uint16{}
47 | } else {
48 | v.V = l.Uint16()
49 | v.Defined = true
50 | }
51 | }
52 |
53 | // MarshalJSON implements a standard json marshaler interface.
54 | func (v Uint16) MarshalJSON() ([]byte, error) {
55 | w := jwriter.Writer{}
56 | v.MarshalEasyJSON(&w)
57 | return w.Buffer.BuildBytes(), w.Error
58 | }
59 |
60 | // UnmarshalJSON implements a standard json unmarshaler interface.
61 | func (v *Uint16) UnmarshalJSON(data []byte) error {
62 | l := jlexer.Lexer{Data: data}
63 | v.UnmarshalEasyJSON(&l)
64 | return l.Error()
65 | }
66 |
67 | // IsDefined returns whether the value is defined, a function is required so that it can
68 | // be used in an interface.
69 | func (v Uint16) IsDefined() bool {
70 | return v.Defined
71 | }
72 |
73 | // String implements a stringer interface using fmt.Sprint for the value.
74 | func (v Uint16) String() string {
75 | if !v.Defined {
76 | return ""
77 | }
78 | return fmt.Sprint(v.V)
79 | }
80 |
--------------------------------------------------------------------------------
/opt/gotemplate_Uint32.go:
--------------------------------------------------------------------------------
1 | // generated by gotemplate
2 |
3 | package opt
4 |
5 | import (
6 | "fmt"
7 |
8 | "github.com/mailru/easyjson/jlexer"
9 | "github.com/mailru/easyjson/jwriter"
10 | )
11 |
12 | // template type Optional(A)
13 |
14 | // A 'gotemplate'-based type for providing optional semantics without using pointers.
15 | type Uint32 struct {
16 | V uint32
17 | Defined bool
18 | }
19 |
20 | // Creates an optional type with a given value.
21 | func OUint32(v uint32) Uint32 {
22 | return Uint32{V: v, Defined: true}
23 | }
24 |
25 | // Get returns the value or given default in the case the value is undefined.
26 | func (v Uint32) Get(deflt uint32) uint32 {
27 | if !v.Defined {
28 | return deflt
29 | }
30 | return v.V
31 | }
32 |
33 | // MarshalEasyJSON does JSON marshaling using easyjson interface.
34 | func (v Uint32) MarshalEasyJSON(w *jwriter.Writer) {
35 | if v.Defined {
36 | w.Uint32(v.V)
37 | } else {
38 | w.RawString("null")
39 | }
40 | }
41 |
42 | // UnmarshalEasyJSON does JSON unmarshaling using easyjson interface.
43 | func (v *Uint32) UnmarshalEasyJSON(l *jlexer.Lexer) {
44 | if l.IsNull() {
45 | l.Skip()
46 | *v = Uint32{}
47 | } else {
48 | v.V = l.Uint32()
49 | v.Defined = true
50 | }
51 | }
52 |
53 | // MarshalJSON implements a standard json marshaler interface.
54 | func (v Uint32) MarshalJSON() ([]byte, error) {
55 | w := jwriter.Writer{}
56 | v.MarshalEasyJSON(&w)
57 | return w.Buffer.BuildBytes(), w.Error
58 | }
59 |
60 | // UnmarshalJSON implements a standard json unmarshaler interface.
61 | func (v *Uint32) UnmarshalJSON(data []byte) error {
62 | l := jlexer.Lexer{Data: data}
63 | v.UnmarshalEasyJSON(&l)
64 | return l.Error()
65 | }
66 |
67 | // IsDefined returns whether the value is defined, a function is required so that it can
68 | // be used in an interface.
69 | func (v Uint32) IsDefined() bool {
70 | return v.Defined
71 | }
72 |
73 | // String implements a stringer interface using fmt.Sprint for the value.
74 | func (v Uint32) String() string {
75 | if !v.Defined {
76 | return ""
77 | }
78 | return fmt.Sprint(v.V)
79 | }
80 |
--------------------------------------------------------------------------------
/opt/gotemplate_Uint64.go:
--------------------------------------------------------------------------------
1 | // generated by gotemplate
2 |
3 | package opt
4 |
5 | import (
6 | "fmt"
7 |
8 | "github.com/mailru/easyjson/jlexer"
9 | "github.com/mailru/easyjson/jwriter"
10 | )
11 |
12 | // template type Optional(A)
13 |
14 | // A 'gotemplate'-based type for providing optional semantics without using pointers.
15 | type Uint64 struct {
16 | V uint64
17 | Defined bool
18 | }
19 |
20 | // Creates an optional type with a given value.
21 | func OUint64(v uint64) Uint64 {
22 | return Uint64{V: v, Defined: true}
23 | }
24 |
25 | // Get returns the value or given default in the case the value is undefined.
26 | func (v Uint64) Get(deflt uint64) uint64 {
27 | if !v.Defined {
28 | return deflt
29 | }
30 | return v.V
31 | }
32 |
33 | // MarshalEasyJSON does JSON marshaling using easyjson interface.
34 | func (v Uint64) MarshalEasyJSON(w *jwriter.Writer) {
35 | if v.Defined {
36 | w.Uint64(v.V)
37 | } else {
38 | w.RawString("null")
39 | }
40 | }
41 |
42 | // UnmarshalEasyJSON does JSON unmarshaling using easyjson interface.
43 | func (v *Uint64) UnmarshalEasyJSON(l *jlexer.Lexer) {
44 | if l.IsNull() {
45 | l.Skip()
46 | *v = Uint64{}
47 | } else {
48 | v.V = l.Uint64()
49 | v.Defined = true
50 | }
51 | }
52 |
53 | // MarshalJSON implements a standard json marshaler interface.
54 | func (v Uint64) MarshalJSON() ([]byte, error) {
55 | w := jwriter.Writer{}
56 | v.MarshalEasyJSON(&w)
57 | return w.Buffer.BuildBytes(), w.Error
58 | }
59 |
60 | // UnmarshalJSON implements a standard json unmarshaler interface.
61 | func (v *Uint64) UnmarshalJSON(data []byte) error {
62 | l := jlexer.Lexer{Data: data}
63 | v.UnmarshalEasyJSON(&l)
64 | return l.Error()
65 | }
66 |
67 | // IsDefined returns whether the value is defined, a function is required so that it can
68 | // be used in an interface.
69 | func (v Uint64) IsDefined() bool {
70 | return v.Defined
71 | }
72 |
73 | // String implements a stringer interface using fmt.Sprint for the value.
74 | func (v Uint64) String() string {
75 | if !v.Defined {
76 | return ""
77 | }
78 | return fmt.Sprint(v.V)
79 | }
80 |
--------------------------------------------------------------------------------
/opt/optional/opt.go:
--------------------------------------------------------------------------------
1 | // +build none
2 |
3 | package optional
4 |
5 | import (
6 | "fmt"
7 |
8 | "github.com/mailru/easyjson/jlexer"
9 | "github.com/mailru/easyjson/jwriter"
10 | )
11 |
12 | // template type Optional(A)
13 | type A int
14 |
15 | // A 'gotemplate'-based type for providing optional semantics without using pointers.
16 | type Optional struct {
17 | V A
18 | Defined bool
19 | }
20 |
21 | // Creates an optional type with a given value.
22 | func OOptional(v A) Optional {
23 | return Optional{V: v, Defined: true}
24 | }
25 |
26 | // Get returns the value or given default in the case the value is undefined.
27 | func (v Optional) Get(deflt A) A {
28 | if !v.Defined {
29 | return deflt
30 | }
31 | return v.V
32 | }
33 |
34 | // MarshalEasyJSON does JSON marshaling using easyjson interface.
35 | func (v Optional) MarshalEasyJSON(w *jwriter.Writer) {
36 | if v.Defined {
37 | w.Optional(v.V)
38 | } else {
39 | w.RawString("null")
40 | }
41 | }
42 |
43 | // UnmarshalEasyJSON does JSON unmarshaling using easyjson interface.
44 | func (v *Optional) UnmarshalEasyJSON(l *jlexer.Lexer) {
45 | if l.IsNull() {
46 | l.Skip()
47 | *v = Optional{}
48 | } else {
49 | v.V = l.Optional()
50 | v.Defined = true
51 | }
52 | }
53 |
54 | // MarshalJSON implements a standard json marshaler interface.
55 | func (v Optional) MarshalJSON() ([]byte, error) {
56 | w := jwriter.Writer{}
57 | v.MarshalEasyJSON(&w)
58 | return w.Buffer.BuildBytes(), w.Error
59 | }
60 |
61 | // UnmarshalJSON implements a standard json unmarshaler interface.
62 | func (v *Optional) UnmarshalJSON(data []byte) error {
63 | l := jlexer.Lexer{Data: data}
64 | v.UnmarshalEasyJSON(&l)
65 | return l.Error()
66 | }
67 |
68 | // IsDefined returns whether the value is defined, a function is required so that it can
69 | // be used in an interface.
70 | func (v Optional) IsDefined() bool {
71 | return v.Defined
72 | }
73 |
74 | // String implements a stringer interface using fmt.Sprint for the value.
75 | func (v Optional) String() string {
76 | if !v.Defined {
77 | return ""
78 | }
79 | return fmt.Sprint(v.V)
80 | }
81 |
--------------------------------------------------------------------------------
/opt/gotemplate_Float32.go:
--------------------------------------------------------------------------------
1 | // generated by gotemplate
2 |
3 | package opt
4 |
5 | import (
6 | "fmt"
7 |
8 | "github.com/mailru/easyjson/jlexer"
9 | "github.com/mailru/easyjson/jwriter"
10 | )
11 |
12 | // template type Optional(A)
13 |
14 | // A 'gotemplate'-based type for providing optional semantics without using pointers.
15 | type Float32 struct {
16 | V float32
17 | Defined bool
18 | }
19 |
20 | // Creates an optional type with a given value.
21 | func OFloat32(v float32) Float32 {
22 | return Float32{V: v, Defined: true}
23 | }
24 |
25 | // Get returns the value or given default in the case the value is undefined.
26 | func (v Float32) Get(deflt float32) float32 {
27 | if !v.Defined {
28 | return deflt
29 | }
30 | return v.V
31 | }
32 |
33 | // MarshalEasyJSON does JSON marshaling using easyjson interface.
34 | func (v Float32) MarshalEasyJSON(w *jwriter.Writer) {
35 | if v.Defined {
36 | w.Float32(v.V)
37 | } else {
38 | w.RawString("null")
39 | }
40 | }
41 |
42 | // UnmarshalEasyJSON does JSON unmarshaling using easyjson interface.
43 | func (v *Float32) UnmarshalEasyJSON(l *jlexer.Lexer) {
44 | if l.IsNull() {
45 | l.Skip()
46 | *v = Float32{}
47 | } else {
48 | v.V = l.Float32()
49 | v.Defined = true
50 | }
51 | }
52 |
53 | // MarshalJSON implements a standard json marshaler interface.
54 | func (v Float32) MarshalJSON() ([]byte, error) {
55 | w := jwriter.Writer{}
56 | v.MarshalEasyJSON(&w)
57 | return w.Buffer.BuildBytes(), w.Error
58 | }
59 |
60 | // UnmarshalJSON implements a standard json unmarshaler interface.
61 | func (v *Float32) UnmarshalJSON(data []byte) error {
62 | l := jlexer.Lexer{Data: data}
63 | v.UnmarshalEasyJSON(&l)
64 | return l.Error()
65 | }
66 |
67 | // IsDefined returns whether the value is defined, a function is required so that it can
68 | // be used in an interface.
69 | func (v Float32) IsDefined() bool {
70 | return v.Defined
71 | }
72 |
73 | // String implements a stringer interface using fmt.Sprint for the value.
74 | func (v Float32) String() string {
75 | if !v.Defined {
76 | return ""
77 | }
78 | return fmt.Sprint(v.V)
79 | }
80 |
--------------------------------------------------------------------------------
/opt/gotemplate_Float64.go:
--------------------------------------------------------------------------------
1 | // generated by gotemplate
2 |
3 | package opt
4 |
5 | import (
6 | "fmt"
7 |
8 | "github.com/mailru/easyjson/jlexer"
9 | "github.com/mailru/easyjson/jwriter"
10 | )
11 |
12 | // template type Optional(A)
13 |
14 | // A 'gotemplate'-based type for providing optional semantics without using pointers.
15 | type Float64 struct {
16 | V float64
17 | Defined bool
18 | }
19 |
20 | // Creates an optional type with a given value.
21 | func OFloat64(v float64) Float64 {
22 | return Float64{V: v, Defined: true}
23 | }
24 |
25 | // Get returns the value or given default in the case the value is undefined.
26 | func (v Float64) Get(deflt float64) float64 {
27 | if !v.Defined {
28 | return deflt
29 | }
30 | return v.V
31 | }
32 |
33 | // MarshalEasyJSON does JSON marshaling using easyjson interface.
34 | func (v Float64) MarshalEasyJSON(w *jwriter.Writer) {
35 | if v.Defined {
36 | w.Float64(v.V)
37 | } else {
38 | w.RawString("null")
39 | }
40 | }
41 |
42 | // UnmarshalEasyJSON does JSON unmarshaling using easyjson interface.
43 | func (v *Float64) UnmarshalEasyJSON(l *jlexer.Lexer) {
44 | if l.IsNull() {
45 | l.Skip()
46 | *v = Float64{}
47 | } else {
48 | v.V = l.Float64()
49 | v.Defined = true
50 | }
51 | }
52 |
53 | // MarshalJSON implements a standard json marshaler interface.
54 | func (v Float64) MarshalJSON() ([]byte, error) {
55 | w := jwriter.Writer{}
56 | v.MarshalEasyJSON(&w)
57 | return w.Buffer.BuildBytes(), w.Error
58 | }
59 |
60 | // UnmarshalJSON implements a standard json unmarshaler interface.
61 | func (v *Float64) UnmarshalJSON(data []byte) error {
62 | l := jlexer.Lexer{Data: data}
63 | v.UnmarshalEasyJSON(&l)
64 | return l.Error()
65 | }
66 |
67 | // IsDefined returns whether the value is defined, a function is required so that it can
68 | // be used in an interface.
69 | func (v Float64) IsDefined() bool {
70 | return v.Defined
71 | }
72 |
73 | // String implements a stringer interface using fmt.Sprint for the value.
74 | func (v Float64) String() string {
75 | if !v.Defined {
76 | return ""
77 | }
78 | return fmt.Sprint(v.V)
79 | }
80 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | VERSION := $(shell git describe --tags --always --dirty)
2 | COMMIT := $(shell git rev-parse --short HEAD)
3 |
4 | all: test
5 |
6 | clean:
7 | rm -rf bin
8 | rm -rf tests/*_easyjson.go
9 | rm -rf benchmark/*_easyjson.go
10 |
11 | build:
12 | go build -ldflags="-s -w -X 'main.Version=$(VERSION)' -X 'main.Commit=$(COMMIT)'" -o ./bin/easyjson ./easyjson
13 |
14 |
15 | generate: build
16 | bin/easyjson -stubs \
17 | ./tests/snake.go \
18 | ./tests/data.go \
19 | ./tests/omitempty.go \
20 | ./tests/nothing.go \
21 | ./tests/named_type.go \
22 | ./tests/custom_map_key_type.go \
23 | ./tests/embedded_type.go \
24 | ./tests/reference_to_pointer.go \
25 | ./tests/html.go \
26 | ./tests/unknown_fields.go \
27 | ./tests/type_declaration.go \
28 | ./tests/type_declaration_skip.go \
29 | ./tests/members_escaped.go \
30 | ./tests/members_unescaped.go \
31 | ./tests/intern.go \
32 | ./tests/nocopy.go \
33 | ./tests/escaping.go
34 | bin/easyjson -all \
35 | ./tests/data.go \
36 | ./tests/nothing.go \
37 | ./tests/errors.go \
38 | ./tests/html.go \
39 | ./tests/type_declaration_skip.go
40 | bin/easyjson \
41 | ./tests/nested_easy.go \
42 | ./tests/named_type.go \
43 | ./tests/custom_map_key_type.go \
44 | ./tests/embedded_type.go \
45 | ./tests/reference_to_pointer.go \
46 | ./tests/key_marshaler_map.go \
47 | ./tests/unknown_fields.go \
48 | ./tests/type_declaration.go \
49 | ./tests/members_escaped.go \
50 | ./tests/intern.go \
51 | ./tests/nocopy.go \
52 | ./tests/escaping.go \
53 | ./tests/nested_marshaler.go \
54 | ./tests/text_marshaler.go
55 | bin/easyjson -snake_case ./tests/snake.go
56 | bin/easyjson -omit_empty ./tests/omitempty.go
57 | bin/easyjson -build_tags=use_easyjson -disable_members_unescape ./benchmark/data.go
58 | bin/easyjson -disallow_unknown_fields ./tests/disallow_unknown.go
59 | bin/easyjson -disable_members_unescape ./tests/members_unescaped.go
60 |
61 | test: generate
62 | go test \
63 | ./tests \
64 | ./jlexer \
65 | ./gen \
66 | ./buffer
67 | cd benchmark && go test -benchmem -tags use_easyjson -bench .
68 | golint -set_exit_status ./tests/*_easyjson.go
69 |
70 | bench-other: generate
71 | cd benchmark && make
72 |
73 | bench-python:
74 | benchmark/ujson.sh
75 |
76 |
77 | .PHONY: clean generate test build
78 |
--------------------------------------------------------------------------------
/buffer/pool_test.go:
--------------------------------------------------------------------------------
1 | package buffer
2 |
3 | import (
4 | "bytes"
5 | "testing"
6 | )
7 |
8 | func TestAppendByte(t *testing.T) {
9 | var b Buffer
10 | var want []byte
11 |
12 | for i := 0; i < 1000; i++ {
13 | b.AppendByte(1)
14 | b.AppendByte(2)
15 | want = append(want, 1, 2)
16 | }
17 |
18 | got := b.BuildBytes()
19 | if !bytes.Equal(got, want) {
20 | t.Errorf("BuildBytes() = %v; want %v", got, want)
21 | }
22 | }
23 |
24 | func TestAppendBytes(t *testing.T) {
25 | var b Buffer
26 | var want []byte
27 |
28 | for i := 0; i < 1000; i++ {
29 | b.AppendBytes([]byte{1, 2})
30 | want = append(want, 1, 2)
31 | }
32 |
33 | got := b.BuildBytes()
34 | if !bytes.Equal(got, want) {
35 | t.Errorf("BuildBytes() = %v; want %v", got, want)
36 | }
37 | }
38 |
39 | func TestAppendString(t *testing.T) {
40 | var b Buffer
41 | var want []byte
42 |
43 | s := "test"
44 | for i := 0; i < 1000; i++ {
45 | b.AppendString(s)
46 | want = append(want, s...)
47 | }
48 |
49 | got := b.BuildBytes()
50 | if !bytes.Equal(got, want) {
51 | t.Errorf("BuildBytes() = %v; want %v", got, want)
52 | }
53 | }
54 |
55 | func TestDumpTo(t *testing.T) {
56 | var b Buffer
57 | var want []byte
58 |
59 | s := "test"
60 | for i := 0; i < 1000; i++ {
61 | b.AppendBytes([]byte(s))
62 | want = append(want, s...)
63 | }
64 |
65 | out := &bytes.Buffer{}
66 | n, err := b.DumpTo(out)
67 | if err != nil {
68 | t.Errorf("DumpTo() error: %v", err)
69 | }
70 |
71 | got := out.Bytes()
72 | if !bytes.Equal(got, want) {
73 | t.Errorf("DumpTo(): got %v; want %v", got, want)
74 | }
75 |
76 | if n != len(want) {
77 | t.Errorf("DumpTo() = %v; want %v", n, len(want))
78 | }
79 | }
80 |
81 | func TestReadCloser(t *testing.T) {
82 | var b Buffer
83 | var want []byte
84 |
85 | s := "test"
86 | for i := 0; i < 1000; i++ {
87 | b.AppendBytes([]byte(s))
88 | want = append(want, s...)
89 | }
90 |
91 | out := &bytes.Buffer{}
92 | rc := b.ReadCloser()
93 | n, err := out.ReadFrom(rc)
94 | if err != nil {
95 | t.Errorf("ReadCloser() error: %v", err)
96 | }
97 | rc.Close() // Will always return nil
98 |
99 | got := out.Bytes()
100 | if !bytes.Equal(got, want) {
101 | t.Errorf("DumpTo(): got %v; want %v", got, want)
102 | }
103 |
104 | if n != int64(len(want)) {
105 | t.Errorf("DumpTo() = %v; want %v", n, len(want))
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/gen/generator_test.go:
--------------------------------------------------------------------------------
1 | package gen
2 |
3 | import (
4 | "testing"
5 | )
6 |
7 | func TestCamelToSnake(t *testing.T) {
8 | for i, test := range []struct {
9 | In, Out string
10 | }{
11 | {"", ""},
12 | {"A", "a"},
13 | {"SimpleExample", "simple_example"},
14 | {"internalField", "internal_field"},
15 |
16 | {"SomeHTTPStuff", "some_http_stuff"},
17 | {"WriteJSON", "write_json"},
18 | {"HTTP2Server", "http2_server"},
19 | {"Some_Mixed_Case", "some_mixed_case"},
20 | {"do_nothing", "do_nothing"},
21 |
22 | {"JSONHTTPRPCServer", "jsonhttprpc_server"}, // nothing can be done here without a dictionary
23 | } {
24 | got := camelToSnake(test.In)
25 | if got != test.Out {
26 | t.Errorf("[%d] camelToSnake(%s) = %s; want %s", i, test.In, got, test.Out)
27 | }
28 | }
29 | }
30 |
31 | func TestCamelToLowerCamel(t *testing.T) {
32 | for i, test := range []struct {
33 | In, Out string
34 | }{
35 | {"", ""},
36 | {"A", "a"},
37 | {"SimpleExample", "simpleExample"},
38 | {"internalField", "internalField"},
39 |
40 | {"SomeHTTPStuff", "someHTTPStuff"},
41 | {"WriteJSON", "writeJSON"},
42 | {"HTTP2Server", "http2Server"},
43 |
44 | {"JSONHTTPRPCServer", "jsonhttprpcServer"}, // nothing can be done here without a dictionary
45 | } {
46 | got := lowerFirst(test.In)
47 | if got != test.Out {
48 | t.Errorf("[%d] lowerFirst(%s) = %s; want %s", i, test.In, got, test.Out)
49 | }
50 | }
51 | }
52 |
53 | func TestJoinFunctionNameParts(t *testing.T) {
54 | for i, test := range []struct {
55 | keepFirst bool
56 | parts []string
57 | out string
58 | }{
59 | {false, []string{}, ""},
60 | {false, []string{"a"}, "A"},
61 | {false, []string{"simple", "example"}, "SimpleExample"},
62 | {true, []string{"first", "example"}, "firstExample"},
63 | {false, []string{"some", "UPPER", "case"}, "SomeUPPERCase"},
64 | {false, []string{"number", "123"}, "Number123"},
65 | } {
66 | got := joinFunctionNameParts(test.keepFirst, test.parts...)
67 | if got != test.out {
68 | t.Errorf("[%d] joinFunctionNameParts(%v) = %s; want %s", i, test.parts, got, test.out)
69 | }
70 | }
71 | }
72 |
73 | func TestFixVendorPath(t *testing.T) {
74 | for i, test := range []struct {
75 | In, Out string
76 | }{
77 | {"", ""},
78 | {"time", "time"},
79 | {"project/vendor/subpackage", "subpackage"},
80 | } {
81 | got := fixPkgPathVendoring(test.In)
82 | if got != test.Out {
83 | t.Errorf("[%d] fixPkgPathVendoring(%s) = %s; want %s", i, test.In, got, test.Out)
84 | }
85 | }
86 |
87 | }
88 |
--------------------------------------------------------------------------------
/benchmark/default_test.go:
--------------------------------------------------------------------------------
1 | // +build !use_easyjson,!use_ffjson,!use_codec,!use_jsoniter
2 |
3 | package benchmark
4 |
5 | import (
6 | "encoding/json"
7 | "testing"
8 | )
9 |
10 | func BenchmarkStd_Unmarshal_M(b *testing.B) {
11 | b.SetBytes(int64(len(largeStructText)))
12 | for i := 0; i < b.N; i++ {
13 | var s LargeStruct
14 | err := json.Unmarshal(largeStructText, &s)
15 | if err != nil {
16 | b.Error(err)
17 | }
18 | }
19 | }
20 |
21 | func BenchmarkStd_Unmarshal_S(b *testing.B) {
22 | for i := 0; i < b.N; i++ {
23 | var s Entities
24 | err := json.Unmarshal(smallStructText, &s)
25 | if err != nil {
26 | b.Error(err)
27 | }
28 | }
29 | b.SetBytes(int64(len(smallStructText)))
30 | }
31 |
32 | func BenchmarkStd_Marshal_M(b *testing.B) {
33 | var l int64
34 | for i := 0; i < b.N; i++ {
35 | data, err := json.Marshal(&largeStructData)
36 | if err != nil {
37 | b.Error(err)
38 | }
39 | l = int64(len(data))
40 | }
41 | b.SetBytes(l)
42 | }
43 |
44 | func BenchmarkStd_Marshal_L(b *testing.B) {
45 | var l int64
46 | for i := 0; i < b.N; i++ {
47 | data, err := json.Marshal(&xlStructData)
48 | if err != nil {
49 | b.Error(err)
50 | }
51 | l = int64(len(data))
52 | }
53 | b.SetBytes(l)
54 | }
55 |
56 | func BenchmarkStd_Marshal_M_Parallel(b *testing.B) {
57 | var l int64
58 | b.RunParallel(func(pb *testing.PB) {
59 | for pb.Next() {
60 | data, err := json.Marshal(&largeStructData)
61 | if err != nil {
62 | b.Error(err)
63 | }
64 | l = int64(len(data))
65 | }
66 | })
67 | b.SetBytes(l)
68 | }
69 |
70 | func BenchmarkStd_Marshal_L_Parallel(b *testing.B) {
71 | var l int64
72 | b.RunParallel(func(pb *testing.PB) {
73 | for pb.Next() {
74 | data, err := json.Marshal(&xlStructData)
75 | if err != nil {
76 | b.Error(err)
77 | }
78 | l = int64(len(data))
79 | }
80 | })
81 | b.SetBytes(l)
82 | }
83 |
84 | func BenchmarkStd_Marshal_S(b *testing.B) {
85 | var l int64
86 | for i := 0; i < b.N; i++ {
87 | data, err := json.Marshal(&smallStructData)
88 | if err != nil {
89 | b.Error(err)
90 | }
91 | l = int64(len(data))
92 | }
93 | b.SetBytes(l)
94 | }
95 |
96 | func BenchmarkStd_Marshal_S_Parallel(b *testing.B) {
97 | var l int64
98 | b.RunParallel(func(pb *testing.PB) {
99 | for pb.Next() {
100 | data, err := json.Marshal(&smallStructData)
101 | if err != nil {
102 | b.Error(err)
103 | }
104 | l = int64(len(data))
105 | }
106 | })
107 | b.SetBytes(l)
108 | }
109 |
110 | func BenchmarkStd_Marshal_M_ToWriter(b *testing.B) {
111 | enc := json.NewEncoder(&DummyWriter{})
112 | for i := 0; i < b.N; i++ {
113 | err := enc.Encode(&largeStructData)
114 | if err != nil {
115 | b.Error(err)
116 | }
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/benchmark/jsoniter_test.go:
--------------------------------------------------------------------------------
1 | // +build use_jsoniter
2 |
3 | package benchmark
4 |
5 | import (
6 | "testing"
7 |
8 | jsoniter "github.com/json-iterator/go"
9 | )
10 |
11 | func BenchmarkJI_Unmarshal_M(b *testing.B) {
12 | b.SetBytes(int64(len(largeStructText)))
13 | for i := 0; i < b.N; i++ {
14 | var s LargeStruct
15 | err := jsoniter.Unmarshal(largeStructText, &s)
16 | if err != nil {
17 | b.Error(err)
18 | }
19 | }
20 | }
21 |
22 | func BenchmarkJI_Unmarshal_S(b *testing.B) {
23 | for i := 0; i < b.N; i++ {
24 | var s Entities
25 | err := jsoniter.Unmarshal(smallStructText, &s)
26 | if err != nil {
27 | b.Error(err)
28 | }
29 | }
30 | b.SetBytes(int64(len(smallStructText)))
31 | }
32 |
33 | func BenchmarkJI_Marshal_M(b *testing.B) {
34 | var l int64
35 | for i := 0; i < b.N; i++ {
36 | data, err := jsoniter.Marshal(&largeStructData)
37 | if err != nil {
38 | b.Error(err)
39 | }
40 | l = int64(len(data))
41 | }
42 | b.SetBytes(l)
43 | }
44 |
45 | func BenchmarkJI_Marshal_L(b *testing.B) {
46 | var l int64
47 | for i := 0; i < b.N; i++ {
48 | data, err := jsoniter.Marshal(&xlStructData)
49 | if err != nil {
50 | b.Error(err)
51 | }
52 | l = int64(len(data))
53 | }
54 | b.SetBytes(l)
55 | }
56 |
57 | func BenchmarkJI_Marshal_M_Parallel(b *testing.B) {
58 | var l int64
59 | b.RunParallel(func(pb *testing.PB) {
60 | for pb.Next() {
61 | data, err := jsoniter.Marshal(&largeStructData)
62 | if err != nil {
63 | b.Error(err)
64 | }
65 | l = int64(len(data))
66 | }
67 | })
68 | b.SetBytes(l)
69 | }
70 |
71 | func BenchmarkJI_Marshal_L_Parallel(b *testing.B) {
72 | var l int64
73 | b.RunParallel(func(pb *testing.PB) {
74 | for pb.Next() {
75 | data, err := jsoniter.Marshal(&xlStructData)
76 | if err != nil {
77 | b.Error(err)
78 | }
79 | l = int64(len(data))
80 | }
81 | })
82 | b.SetBytes(l)
83 | }
84 |
85 | func BenchmarkJI_Marshal_S(b *testing.B) {
86 | var l int64
87 | for i := 0; i < b.N; i++ {
88 | data, err := jsoniter.Marshal(&smallStructData)
89 | if err != nil {
90 | b.Error(err)
91 | }
92 | l = int64(len(data))
93 | }
94 | b.SetBytes(l)
95 | }
96 |
97 | func BenchmarkJI_Marshal_S_Parallel(b *testing.B) {
98 | var l int64
99 | b.RunParallel(func(pb *testing.PB) {
100 | for pb.Next() {
101 | data, err := jsoniter.Marshal(&smallStructData)
102 | if err != nil {
103 | b.Error(err)
104 | }
105 | l = int64(len(data))
106 | }
107 | })
108 | b.SetBytes(l)
109 | }
110 |
111 | func BenchmarkJI_Marshal_M_ToWriter(b *testing.B) {
112 | enc := jsoniter.NewEncoder(&DummyWriter{})
113 | for i := 0; i < b.N; i++ {
114 | err := enc.Encode(&largeStructData)
115 | if err != nil {
116 | b.Error(err)
117 | }
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/parser/modulepath.go:
--------------------------------------------------------------------------------
1 | package parser
2 |
3 | import (
4 | "bytes"
5 | "strconv"
6 | )
7 |
8 | // Content of this file was copied from the package golang.org/x/mod/modfile
9 | // https://github.com/golang/mod/blob/v0.2.0/modfile/read.go#L877
10 | // Under the BSD-3-Clause licence:
11 | // golang.org/x/mod@v0.2.0/LICENSE
12 | /*
13 | Copyright (c) 2009 The Go Authors. All rights reserved.
14 |
15 | Redistribution and use in source and binary forms, with or without
16 | modification, are permitted provided that the following conditions are
17 | met:
18 |
19 | * Redistributions of source code must retain the above copyright
20 | notice, this list of conditions and the following disclaimer.
21 | * Redistributions in binary form must reproduce the above
22 | copyright notice, this list of conditions and the following disclaimer
23 | in the documentation and/or other materials provided with the
24 | distribution.
25 | * Neither the name of Google Inc. nor the names of its
26 | contributors may be used to endorse or promote products derived from
27 | this software without specific prior written permission.
28 |
29 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
30 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
31 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
32 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
33 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
34 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
35 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
36 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
37 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
38 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
39 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40 | */
41 |
42 | var (
43 | slashSlash = []byte("//")
44 | moduleStr = []byte("module")
45 | )
46 |
47 | // modulePath returns the module path from the gomod file text.
48 | // If it cannot find a module path, it returns an empty string.
49 | // It is tolerant of unrelated problems in the go.mod file.
50 | func modulePath(mod []byte) string {
51 | for len(mod) > 0 {
52 | line := mod
53 | mod = nil
54 | if i := bytes.IndexByte(line, '\n'); i >= 0 {
55 | line, mod = line[:i], line[i+1:]
56 | }
57 | if i := bytes.Index(line, slashSlash); i >= 0 {
58 | line = line[:i]
59 | }
60 | line = bytes.TrimSpace(line)
61 | if !bytes.HasPrefix(line, moduleStr) {
62 | continue
63 | }
64 | line = line[len(moduleStr):]
65 | n := len(line)
66 | line = bytes.TrimSpace(line)
67 | if len(line) == n || len(line) == 0 {
68 | continue
69 | }
70 |
71 | if line[0] == '"' || line[0] == '`' {
72 | p, err := strconv.Unquote(string(line))
73 | if err != nil {
74 | return "" // malformed quoted string or multiline module path
75 | }
76 | return p
77 | }
78 |
79 | return string(line)
80 | }
81 | return "" // missing module path
82 | }
83 |
--------------------------------------------------------------------------------
/parser/parser.go:
--------------------------------------------------------------------------------
1 | package parser
2 |
3 | import (
4 | "go/ast"
5 | "go/parser"
6 | "go/token"
7 | "os"
8 | "strings"
9 | )
10 |
11 | const (
12 | structComment = "easyjson:json"
13 | structSkipComment = "easyjson:skip"
14 | )
15 |
16 | type Parser struct {
17 | PkgPath string
18 | PkgName string
19 | StructNames []string
20 | AllStructs bool
21 | }
22 |
23 | type visitor struct {
24 | *Parser
25 |
26 | name string
27 | }
28 |
29 | func (p *Parser) needType(comments *ast.CommentGroup) (skip, explicit bool) {
30 | if comments == nil {
31 | return
32 | }
33 |
34 | for _, v := range comments.List {
35 | comment := v.Text
36 |
37 | if len(comment) > 2 {
38 | switch comment[1] {
39 | case '/':
40 | // -style comment (no newline at the end)
41 | comment = comment[2:]
42 | case '*':
43 | /*-style comment */
44 | comment = comment[2 : len(comment)-2]
45 | }
46 | }
47 |
48 | for _, comment := range strings.Split(comment, "\n") {
49 | comment = strings.TrimSpace(comment)
50 |
51 | if strings.HasPrefix(comment, structSkipComment) {
52 | return true, false
53 | }
54 | if strings.HasPrefix(comment, structComment) {
55 | return false, true
56 | }
57 | }
58 | }
59 |
60 | return
61 | }
62 |
63 | func (v *visitor) Visit(n ast.Node) (w ast.Visitor) {
64 | switch n := n.(type) {
65 | case *ast.Package:
66 | return v
67 | case *ast.File:
68 | v.PkgName = n.Name.String()
69 | return v
70 |
71 | case *ast.GenDecl:
72 | skip, explicit := v.needType(n.Doc)
73 |
74 | if skip || explicit {
75 | for _, nc := range n.Specs {
76 | switch nct := nc.(type) {
77 | case *ast.TypeSpec:
78 | nct.Doc = n.Doc
79 | }
80 | }
81 | }
82 |
83 | return v
84 | case *ast.TypeSpec:
85 | skip, explicit := v.needType(n.Doc)
86 | if skip {
87 | return nil
88 | }
89 | if !explicit && !v.AllStructs {
90 | return nil
91 | }
92 |
93 | v.name = n.Name.String()
94 |
95 | // Allow to specify non-structs explicitly independent of '-all' flag.
96 | if explicit {
97 | v.StructNames = append(v.StructNames, v.name)
98 | return nil
99 | }
100 |
101 | return v
102 | case *ast.StructType:
103 | v.StructNames = append(v.StructNames, v.name)
104 | return nil
105 | }
106 | return nil
107 | }
108 |
109 | func (p *Parser) Parse(fname string, isDir bool) error {
110 | var err error
111 | if p.PkgPath, err = getPkgPath(fname, isDir); err != nil {
112 | return err
113 | }
114 |
115 | fset := token.NewFileSet()
116 | if isDir {
117 | packages, err := parser.ParseDir(fset, fname, excludeTestFiles, parser.ParseComments)
118 | if err != nil {
119 | return err
120 | }
121 |
122 | for _, pckg := range packages {
123 | ast.Walk(&visitor{Parser: p}, pckg)
124 | }
125 | } else {
126 | f, err := parser.ParseFile(fset, fname, nil, parser.ParseComments)
127 | if err != nil {
128 | return err
129 | }
130 |
131 | ast.Walk(&visitor{Parser: p}, f)
132 | }
133 | return nil
134 | }
135 |
136 | func excludeTestFiles(fi os.FileInfo) bool {
137 | return !strings.HasSuffix(fi.Name(), "_test.go")
138 | }
139 |
--------------------------------------------------------------------------------
/benchmark/go.sum:
--------------------------------------------------------------------------------
1 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
2 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
3 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
4 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
5 | github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
6 | github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
7 | github.com/json-iterator/go v1.1.7 h1:KfgG9LzI+pYjr4xvmz/5H4FXjokeP+rlHLhv3iH62Fo=
8 | github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
9 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
10 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
11 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
12 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
13 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
14 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
15 | github.com/pquerna/ffjson v0.0.0-20190813045741-dac163c6c0a9 h1:kyf9snWXHvQc+yxE9imhdI8YAm4oKeZISlaAR+x73zs=
16 | github.com/pquerna/ffjson v0.0.0-20190813045741-dac163c6c0a9/go.mod h1:YARuvh7BUWHNhzDq2OM5tzR2RiCcN2D7sapiKyCel/M=
17 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
18 | github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
19 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
20 | github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
21 | github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
22 | github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
23 | github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
24 | github.com/ugorji/go/codec/codecgen v1.1.7 h1:6BU4y9NIgvVMNetSGxkH9lOOa2UB5b8sCHC6+8m5lVc=
25 | github.com/ugorji/go/codec/codecgen v1.1.7/go.mod h1:FMgcDIjFRtjQPUVM3GN592ic8epl2qsvfNPhezMgP8Y=
26 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
27 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
28 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
29 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
30 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
31 | golang.org/x/tools v0.0.0-20190829051458-42f498d34c4d h1:yqT69RdmShXXRtsT9jS6Iy0FFLWGLCe3IqGE0vsP0m4=
32 | golang.org/x/tools v0.0.0-20190829051458-42f498d34c4d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
33 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
34 |
--------------------------------------------------------------------------------
/helpers.go:
--------------------------------------------------------------------------------
1 | // Package easyjson contains marshaler/unmarshaler interfaces and helper functions.
2 | package easyjson
3 |
4 | import (
5 | "io"
6 | "io/ioutil"
7 | "net/http"
8 | "strconv"
9 | "unsafe"
10 |
11 | "github.com/mailru/easyjson/jlexer"
12 | "github.com/mailru/easyjson/jwriter"
13 | )
14 |
15 | // Marshaler is an easyjson-compatible marshaler interface.
16 | type Marshaler interface {
17 | MarshalEasyJSON(w *jwriter.Writer)
18 | }
19 |
20 | // Unmarshaler is an easyjson-compatible unmarshaler interface.
21 | type Unmarshaler interface {
22 | UnmarshalEasyJSON(w *jlexer.Lexer)
23 | }
24 |
25 | // MarshalerUnmarshaler is an easyjson-compatible marshaler/unmarshaler interface.
26 | type MarshalerUnmarshaler interface {
27 | Marshaler
28 | Unmarshaler
29 | }
30 |
31 | // Optional defines an undefined-test method for a type to integrate with 'omitempty' logic.
32 | type Optional interface {
33 | IsDefined() bool
34 | }
35 |
36 | // UnknownsUnmarshaler provides a method to unmarshal unknown struct fileds and save them as you want
37 | type UnknownsUnmarshaler interface {
38 | UnmarshalUnknown(in *jlexer.Lexer, key string)
39 | }
40 |
41 | // UnknownsMarshaler provides a method to write additional struct fields
42 | type UnknownsMarshaler interface {
43 | MarshalUnknowns(w *jwriter.Writer, first bool)
44 | }
45 |
46 | func isNilInterface(i interface{}) bool {
47 | return (*[2]uintptr)(unsafe.Pointer(&i))[1] == 0
48 | }
49 |
50 | // Marshal returns data as a single byte slice. Method is suboptimal as the data is likely to be copied
51 | // from a chain of smaller chunks.
52 | func Marshal(v Marshaler) ([]byte, error) {
53 | if isNilInterface(v) {
54 | return nullBytes, nil
55 | }
56 |
57 | w := jwriter.Writer{}
58 | v.MarshalEasyJSON(&w)
59 | return w.BuildBytes()
60 | }
61 |
62 | // MarshalToWriter marshals the data to an io.Writer.
63 | func MarshalToWriter(v Marshaler, w io.Writer) (written int, err error) {
64 | if isNilInterface(v) {
65 | return w.Write(nullBytes)
66 | }
67 |
68 | jw := jwriter.Writer{}
69 | v.MarshalEasyJSON(&jw)
70 | return jw.DumpTo(w)
71 | }
72 |
73 | // MarshalToHTTPResponseWriter sets Content-Length and Content-Type headers for the
74 | // http.ResponseWriter, and send the data to the writer. started will be equal to
75 | // false if an error occurred before any http.ResponseWriter methods were actually
76 | // invoked (in this case a 500 reply is possible).
77 | func MarshalToHTTPResponseWriter(v Marshaler, w http.ResponseWriter) (started bool, written int, err error) {
78 | if isNilInterface(v) {
79 | w.Header().Set("Content-Type", "application/json")
80 | w.Header().Set("Content-Length", strconv.Itoa(len(nullBytes)))
81 | written, err = w.Write(nullBytes)
82 | return true, written, err
83 | }
84 |
85 | jw := jwriter.Writer{}
86 | v.MarshalEasyJSON(&jw)
87 | if jw.Error != nil {
88 | return false, 0, jw.Error
89 | }
90 | w.Header().Set("Content-Type", "application/json")
91 | w.Header().Set("Content-Length", strconv.Itoa(jw.Size()))
92 |
93 | started = true
94 | written, err = jw.DumpTo(w)
95 | return
96 | }
97 |
98 | // Unmarshal decodes the JSON in data into the object.
99 | func Unmarshal(data []byte, v Unmarshaler) error {
100 | l := jlexer.Lexer{Data: data}
101 | v.UnmarshalEasyJSON(&l)
102 | return l.Error()
103 | }
104 |
105 | // UnmarshalFromReader reads all the data in the reader and decodes as JSON into the object.
106 | func UnmarshalFromReader(r io.Reader, v Unmarshaler) error {
107 | data, err := ioutil.ReadAll(r)
108 | if err != nil {
109 | return err
110 | }
111 | l := jlexer.Lexer{Data: data}
112 | v.UnmarshalEasyJSON(&l)
113 | return l.Error()
114 | }
115 |
--------------------------------------------------------------------------------
/parser/pkgpath.go:
--------------------------------------------------------------------------------
1 | package parser
2 |
3 | import (
4 | "bytes"
5 | "fmt"
6 | "go/build"
7 | "io/ioutil"
8 | "os"
9 | "os/exec"
10 | "path"
11 | "path/filepath"
12 | "strings"
13 | "sync"
14 | )
15 |
16 | func getPkgPath(fname string, isDir bool) (string, error) {
17 | if !filepath.IsAbs(fname) {
18 | pwd, err := os.Getwd()
19 | if err != nil {
20 | return "", err
21 | }
22 | fname = filepath.Join(pwd, fname)
23 | }
24 |
25 | goModPath, _ := goModPath(fname, isDir)
26 | if strings.Contains(goModPath, "go.mod") {
27 | pkgPath, err := getPkgPathFromGoMod(fname, isDir, goModPath)
28 | if err != nil {
29 | return "", err
30 | }
31 |
32 | return pkgPath, nil
33 | }
34 |
35 | return getPkgPathFromGOPATH(fname, isDir)
36 | }
37 |
38 | var goModPathCache = struct {
39 | paths map[string]string
40 | sync.RWMutex
41 | }{
42 | paths: make(map[string]string),
43 | }
44 |
45 | // empty if no go.mod, GO111MODULE=off or go without go modules support
46 | func goModPath(fname string, isDir bool) (string, error) {
47 | root := fname
48 | if !isDir {
49 | root = filepath.Dir(fname)
50 | }
51 |
52 | goModPathCache.RLock()
53 | goModPath, ok := goModPathCache.paths[root]
54 | goModPathCache.RUnlock()
55 | if ok {
56 | return goModPath, nil
57 | }
58 |
59 | defer func() {
60 | goModPathCache.Lock()
61 | goModPathCache.paths[root] = goModPath
62 | goModPathCache.Unlock()
63 | }()
64 |
65 | cmd := exec.Command("go", "env", "GOMOD")
66 | cmd.Dir = root
67 |
68 | stdout, err := cmd.Output()
69 | if err != nil {
70 | return "", err
71 | }
72 |
73 | goModPath = string(bytes.TrimSpace(stdout))
74 |
75 | return goModPath, nil
76 | }
77 |
78 | func getPkgPathFromGoMod(fname string, isDir bool, goModPath string) (string, error) {
79 | modulePath := getModulePath(goModPath)
80 | if modulePath == "" {
81 | return "", fmt.Errorf("cannot determine module path from %s", goModPath)
82 | }
83 |
84 | rel := path.Join(modulePath, filePathToPackagePath(strings.TrimPrefix(fname, filepath.Dir(goModPath))))
85 |
86 | if !isDir {
87 | return path.Dir(rel), nil
88 | }
89 |
90 | return path.Clean(rel), nil
91 | }
92 |
93 | var pkgPathFromGoModCache = struct {
94 | paths map[string]string
95 | sync.RWMutex
96 | }{
97 | paths: make(map[string]string),
98 | }
99 |
100 | func getModulePath(goModPath string) string {
101 | pkgPathFromGoModCache.RLock()
102 | pkgPath, ok := pkgPathFromGoModCache.paths[goModPath]
103 | pkgPathFromGoModCache.RUnlock()
104 | if ok {
105 | return pkgPath
106 | }
107 |
108 | defer func() {
109 | pkgPathFromGoModCache.Lock()
110 | pkgPathFromGoModCache.paths[goModPath] = pkgPath
111 | pkgPathFromGoModCache.Unlock()
112 | }()
113 |
114 | data, err := ioutil.ReadFile(goModPath)
115 | if err != nil {
116 | return ""
117 | }
118 | pkgPath = modulePath(data)
119 | return pkgPath
120 | }
121 |
122 | func getPkgPathFromGOPATH(fname string, isDir bool) (string, error) {
123 | gopath := os.Getenv("GOPATH")
124 | if gopath == "" {
125 | gopath = build.Default.GOPATH
126 | }
127 |
128 | for _, p := range strings.Split(gopath, string(filepath.ListSeparator)) {
129 | prefix := filepath.Join(p, "src") + string(filepath.Separator)
130 | rel, err := filepath.Rel(prefix, fname)
131 | if err == nil && !strings.HasPrefix(rel, ".."+string(filepath.Separator)) {
132 | if !isDir {
133 | return path.Dir(filePathToPackagePath(rel)), nil
134 | } else {
135 | return path.Clean(filePathToPackagePath(rel)), nil
136 | }
137 | }
138 | }
139 |
140 | return "", fmt.Errorf("file '%v' is not in GOPATH '%v'", fname, gopath)
141 | }
142 |
143 | func filePathToPackagePath(path string) string {
144 | return filepath.ToSlash(path)
145 | }
146 |
--------------------------------------------------------------------------------
/benchmark/easyjson_test.go:
--------------------------------------------------------------------------------
1 | // +build use_easyjson
2 |
3 | package benchmark
4 |
5 | import (
6 | "testing"
7 |
8 | "github.com/mailru/easyjson"
9 | "github.com/mailru/easyjson/jwriter"
10 | )
11 |
12 | func BenchmarkEJ_Unmarshal_M(b *testing.B) {
13 | b.SetBytes(int64(len(largeStructText)))
14 | for i := 0; i < b.N; i++ {
15 | var s LargeStruct
16 | err := s.UnmarshalJSON(largeStructText)
17 | if err != nil {
18 | b.Error(err)
19 | }
20 | }
21 | }
22 |
23 | func BenchmarkEJ_Unmarshal_S(b *testing.B) {
24 | b.SetBytes(int64(len(smallStructText)))
25 |
26 | for i := 0; i < b.N; i++ {
27 | var s Entities
28 | err := s.UnmarshalJSON(smallStructText)
29 | if err != nil {
30 | b.Error(err)
31 | }
32 | }
33 | }
34 |
35 | func BenchmarkEJ_Marshal_M(b *testing.B) {
36 | var l int64
37 | for i := 0; i < b.N; i++ {
38 | data, err := easyjson.Marshal(&largeStructData)
39 | if err != nil {
40 | b.Error(err)
41 | }
42 | l = int64(len(data))
43 | }
44 | b.SetBytes(l)
45 | }
46 |
47 | func BenchmarkEJ_Marshal_L(b *testing.B) {
48 | var l int64
49 | for i := 0; i < b.N; i++ {
50 | data, err := easyjson.Marshal(&xlStructData)
51 | if err != nil {
52 | b.Error(err)
53 | }
54 | l = int64(len(data))
55 | }
56 | b.SetBytes(l)
57 | }
58 |
59 | func BenchmarkEJ_Marshal_L_ToWriter(b *testing.B) {
60 | var l int64
61 | out := &DummyWriter{}
62 | for i := 0; i < b.N; i++ {
63 | w := jwriter.Writer{}
64 | xlStructData.MarshalEasyJSON(&w)
65 | if w.Error != nil {
66 | b.Error(w.Error)
67 | }
68 |
69 | l = int64(w.Size())
70 | w.DumpTo(out)
71 | }
72 | b.SetBytes(l)
73 |
74 | }
75 | func BenchmarkEJ_Marshal_M_Parallel(b *testing.B) {
76 | b.SetBytes(int64(len(largeStructText)))
77 |
78 | b.RunParallel(func(pb *testing.PB) {
79 | for pb.Next() {
80 | _, err := largeStructData.MarshalJSON()
81 | if err != nil {
82 | b.Error(err)
83 | }
84 | }
85 | })
86 | }
87 |
88 | func BenchmarkEJ_Marshal_M_ToWriter(b *testing.B) {
89 | var l int64
90 | out := &DummyWriter{}
91 | for i := 0; i < b.N; i++ {
92 | w := jwriter.Writer{}
93 | largeStructData.MarshalEasyJSON(&w)
94 | if w.Error != nil {
95 | b.Error(w.Error)
96 | }
97 |
98 | l = int64(w.Size())
99 | w.DumpTo(out)
100 | }
101 | b.SetBytes(l)
102 |
103 | }
104 | func BenchmarkEJ_Marshal_M_ToWriter_Parallel(b *testing.B) {
105 | out := &DummyWriter{}
106 |
107 | b.RunParallel(func(pb *testing.PB) {
108 | var l int64
109 | for pb.Next() {
110 | w := jwriter.Writer{}
111 | largeStructData.MarshalEasyJSON(&w)
112 | if w.Error != nil {
113 | b.Error(w.Error)
114 | }
115 |
116 | l = int64(w.Size())
117 | w.DumpTo(out)
118 | }
119 | if l > 0 {
120 | b.SetBytes(l)
121 | }
122 | })
123 |
124 | }
125 |
126 | func BenchmarkEJ_Marshal_L_Parallel(b *testing.B) {
127 | var l int64
128 | b.RunParallel(func(pb *testing.PB) {
129 | for pb.Next() {
130 | data, err := xlStructData.MarshalJSON()
131 | if err != nil {
132 | b.Error(err)
133 | }
134 | l = int64(len(data))
135 | }
136 | })
137 | b.SetBytes(l)
138 | }
139 |
140 | func BenchmarkEJ_Marshal_L_ToWriter_Parallel(b *testing.B) {
141 | out := &DummyWriter{}
142 | b.RunParallel(func(pb *testing.PB) {
143 | var l int64
144 | for pb.Next() {
145 | w := jwriter.Writer{}
146 |
147 | xlStructData.MarshalEasyJSON(&w)
148 | if w.Error != nil {
149 | b.Error(w.Error)
150 | }
151 | l = int64(w.Size())
152 | w.DumpTo(out)
153 | }
154 | if l > 0 {
155 | b.SetBytes(l)
156 | }
157 | })
158 | }
159 |
160 | func BenchmarkEJ_Marshal_S(b *testing.B) {
161 | var l int64
162 | for i := 0; i < b.N; i++ {
163 | data, err := smallStructData.MarshalJSON()
164 | if err != nil {
165 | b.Error(err)
166 | }
167 | l = int64(len(data))
168 | }
169 | b.SetBytes(l)
170 | }
171 |
172 | func BenchmarkEJ_Marshal_S_Parallel(b *testing.B) {
173 | var l int64
174 | b.RunParallel(func(pb *testing.PB) {
175 | for pb.Next() {
176 | data, err := smallStructData.MarshalJSON()
177 | if err != nil {
178 | b.Error(err)
179 | }
180 | l = int64(len(data))
181 | }
182 | })
183 | b.SetBytes(l)
184 | }
185 |
--------------------------------------------------------------------------------
/benchmark/ffjson_test.go:
--------------------------------------------------------------------------------
1 | // +build use_ffjson
2 |
3 | package benchmark
4 |
5 | import (
6 | "testing"
7 |
8 | "github.com/pquerna/ffjson/ffjson"
9 | )
10 |
11 | func BenchmarkFF_Unmarshal_M(b *testing.B) {
12 | b.SetBytes(int64(len(largeStructText)))
13 | for i := 0; i < b.N; i++ {
14 | var s LargeStruct
15 | err := ffjson.UnmarshalFast(largeStructText, &s)
16 | if err != nil {
17 | b.Error(err)
18 | }
19 | }
20 | }
21 |
22 | func BenchmarkFF_Unmarshal_S(b *testing.B) {
23 | for i := 0; i < b.N; i++ {
24 | var s Entities
25 | err := ffjson.UnmarshalFast(smallStructText, &s)
26 | if err != nil {
27 | b.Error(err)
28 | }
29 | }
30 | b.SetBytes(int64(len(smallStructText)))
31 | }
32 |
33 | func BenchmarkFF_Marshal_M(b *testing.B) {
34 | var l int64
35 | for i := 0; i < b.N; i++ {
36 | data, err := ffjson.MarshalFast(&largeStructData)
37 | if err != nil {
38 | b.Error(err)
39 | }
40 | l = int64(len(data))
41 | }
42 | b.SetBytes(l)
43 | }
44 |
45 | func BenchmarkFF_Marshal_S(b *testing.B) {
46 | var l int64
47 | for i := 0; i < b.N; i++ {
48 | data, err := ffjson.MarshalFast(&smallStructData)
49 | if err != nil {
50 | b.Error(err)
51 | }
52 | l = int64(len(data))
53 | }
54 | b.SetBytes(l)
55 | }
56 |
57 | func BenchmarkFF_Marshal_M_Pool(b *testing.B) {
58 | var l int64
59 | for i := 0; i < b.N; i++ {
60 | data, err := ffjson.MarshalFast(&largeStructData)
61 | if err != nil {
62 | b.Error(err)
63 | }
64 | l = int64(len(data))
65 | ffjson.Pool(data)
66 | }
67 | b.SetBytes(l)
68 | }
69 |
70 | func BenchmarkFF_Marshal_L(b *testing.B) {
71 | var l int64
72 | for i := 0; i < b.N; i++ {
73 | data, err := ffjson.MarshalFast(&xlStructData)
74 | if err != nil {
75 | b.Error(err)
76 | }
77 | l = int64(len(data))
78 | }
79 | b.SetBytes(l)
80 | }
81 |
82 | func BenchmarkFF_Marshal_L_Pool(b *testing.B) {
83 | var l int64
84 | for i := 0; i < b.N; i++ {
85 | data, err := ffjson.MarshalFast(&xlStructData)
86 | if err != nil {
87 | b.Error(err)
88 | }
89 | l = int64(len(data))
90 | ffjson.Pool(data)
91 | }
92 | b.SetBytes(l)
93 | }
94 |
95 | func BenchmarkFF_Marshal_L_Pool_Parallel(b *testing.B) {
96 | var l int64
97 | for i := 0; i < b.N; i++ {
98 | data, err := ffjson.MarshalFast(&xlStructData)
99 | if err != nil {
100 | b.Error(err)
101 | }
102 | l = int64(len(data))
103 | ffjson.Pool(data)
104 | }
105 | b.SetBytes(l)
106 | }
107 | func BenchmarkFF_Marshal_M_Pool_Parallel(b *testing.B) {
108 | var l int64
109 | b.RunParallel(func(pb *testing.PB) {
110 | for pb.Next() {
111 | data, err := ffjson.MarshalFast(&largeStructData)
112 | if err != nil {
113 | b.Error(err)
114 | }
115 | l = int64(len(data))
116 | ffjson.Pool(data)
117 | }
118 | })
119 | b.SetBytes(l)
120 | }
121 |
122 | func BenchmarkFF_Marshal_S_Pool(b *testing.B) {
123 | var l int64
124 | for i := 0; i < b.N; i++ {
125 | data, err := ffjson.MarshalFast(&smallStructData)
126 | if err != nil {
127 | b.Error(err)
128 | }
129 | l = int64(len(data))
130 | ffjson.Pool(data)
131 | }
132 | b.SetBytes(l)
133 | }
134 |
135 | func BenchmarkFF_Marshal_S_Pool_Parallel(b *testing.B) {
136 | var l int64
137 | b.RunParallel(func(pb *testing.PB) {
138 | for pb.Next() {
139 | data, err := ffjson.MarshalFast(&smallStructData)
140 | if err != nil {
141 | b.Error(err)
142 | }
143 | l = int64(len(data))
144 | ffjson.Pool(data)
145 | }
146 | })
147 | b.SetBytes(l)
148 | }
149 |
150 | func BenchmarkFF_Marshal_S_Parallel(b *testing.B) {
151 | var l int64
152 | b.RunParallel(func(pb *testing.PB) {
153 | for pb.Next() {
154 | data, err := ffjson.MarshalFast(&smallStructData)
155 | if err != nil {
156 | b.Error(err)
157 | }
158 | l = int64(len(data))
159 | }
160 | })
161 | b.SetBytes(l)
162 | }
163 |
164 | func BenchmarkFF_Marshal_M_Parallel(b *testing.B) {
165 | var l int64
166 | b.RunParallel(func(pb *testing.PB) {
167 | for pb.Next() {
168 | data, err := ffjson.MarshalFast(&largeStructData)
169 | if err != nil {
170 | b.Error(err)
171 | }
172 | l = int64(len(data))
173 | }
174 | })
175 | b.SetBytes(l)
176 | }
177 |
178 | func BenchmarkFF_Marshal_L_Parallel(b *testing.B) {
179 | var l int64
180 | b.RunParallel(func(pb *testing.PB) {
181 | for pb.Next() {
182 | data, err := ffjson.MarshalFast(&xlStructData)
183 | if err != nil {
184 | b.Error(err)
185 | }
186 | l = int64(len(data))
187 | }
188 | })
189 | b.SetBytes(l)
190 | }
191 |
--------------------------------------------------------------------------------
/easyjson/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "errors"
5 | "flag"
6 | "fmt"
7 | "os"
8 | "path/filepath"
9 | "strings"
10 |
11 | "github.com/mailru/easyjson/bootstrap"
12 | // Reference the gen package to be friendly to vendoring tools,
13 | // as it is an indirect dependency.
14 | // (The temporary bootstrapping code uses it.)
15 | _ "github.com/mailru/easyjson/gen"
16 | "github.com/mailru/easyjson/parser"
17 | )
18 |
19 | var (
20 | Version = "dev" //
21 | Commit = "none"
22 | )
23 |
24 | var buildTags = flag.String("build_tags", "", "build tags to add to generated file")
25 | var genBuildFlags = flag.String("gen_build_flags", "", "build flags when running the generator while bootstrapping")
26 | var snakeCase = flag.Bool("snake_case", false, "use snake_case names instead of CamelCase by default")
27 | var lowerCamelCase = flag.Bool("lower_camel_case", false, "use lowerCamelCase names instead of CamelCase by default")
28 | var noStdMarshalers = flag.Bool("no_std_marshalers", false, "don't generate MarshalJSON/UnmarshalJSON funcs")
29 | var omitEmpty = flag.Bool("omit_empty", false, "omit empty fields by default")
30 | var allStructs = flag.Bool("all", false, "generate marshaler/unmarshalers for all structs in a file")
31 | var simpleBytes = flag.Bool("byte", false, "use simple bytes instead of Base64Bytes for slice of bytes")
32 | var leaveTemps = flag.Bool("leave_temps", false, "do not delete temporary files")
33 | var stubs = flag.Bool("stubs", false, "only generate stubs for marshaler/unmarshaler funcs")
34 | var noformat = flag.Bool("noformat", false, "do not run 'gofmt -w' on output file")
35 | var specifiedName = flag.String("output_filename", "", "specify the filename of the output")
36 | var processPkg = flag.Bool("pkg", false, "process the whole package instead of just the given file")
37 | var disallowUnknownFields = flag.Bool("disallow_unknown_fields", false, "return error if any unknown field in json appeared")
38 | var skipMemberNameUnescaping = flag.Bool("disable_members_unescape", false, "don't perform unescaping of member names to improve performance")
39 | var showVersion = flag.Bool("version", false, "print version and exit")
40 |
41 | func generate(fname string) (err error) {
42 | fInfo, err := os.Stat(fname)
43 | if err != nil {
44 | return err
45 | }
46 |
47 | p := parser.Parser{AllStructs: *allStructs}
48 | if err := p.Parse(fname, fInfo.IsDir()); err != nil {
49 | return fmt.Errorf("Error parsing %v: %v", fname, err)
50 | }
51 |
52 | var outName string
53 | if fInfo.IsDir() {
54 | outName = filepath.Join(fname, p.PkgName+"_easyjson.go")
55 | } else {
56 | if s := strings.TrimSuffix(fname, ".go"); s == fname {
57 | return errors.New("Filename must end in '.go'")
58 | } else {
59 | outName = s + "_easyjson.go"
60 | }
61 | }
62 |
63 | if *specifiedName != "" {
64 | outName = *specifiedName
65 | }
66 |
67 | var trimmedBuildTags string
68 | if *buildTags != "" {
69 | trimmedBuildTags = strings.TrimSpace(*buildTags)
70 | }
71 |
72 | var trimmedGenBuildFlags string
73 | if *genBuildFlags != "" {
74 | trimmedGenBuildFlags = strings.TrimSpace(*genBuildFlags)
75 | }
76 |
77 | g := bootstrap.Generator{
78 | BuildTags: trimmedBuildTags,
79 | GenBuildFlags: trimmedGenBuildFlags,
80 | PkgPath: p.PkgPath,
81 | PkgName: p.PkgName,
82 | Types: p.StructNames,
83 | SnakeCase: *snakeCase,
84 | LowerCamelCase: *lowerCamelCase,
85 | NoStdMarshalers: *noStdMarshalers,
86 | DisallowUnknownFields: *disallowUnknownFields,
87 | SkipMemberNameUnescaping: *skipMemberNameUnescaping,
88 | OmitEmpty: *omitEmpty,
89 | LeaveTemps: *leaveTemps,
90 | OutName: outName,
91 | StubsOnly: *stubs,
92 | NoFormat: *noformat,
93 | SimpleBytes: *simpleBytes,
94 | }
95 |
96 | if err := g.Run(); err != nil {
97 | return fmt.Errorf("Bootstrap failed: %v", err)
98 | }
99 | return nil
100 | }
101 |
102 | func main() {
103 | flag.Parse()
104 |
105 | files := flag.Args()
106 |
107 | if *showVersion {
108 | fmt.Printf("easyjson generator\nversion: %s\ncommit: %s\n", Version, Commit)
109 | os.Exit(0)
110 | }
111 |
112 | gofile := os.Getenv("GOFILE")
113 | if *processPkg {
114 | gofile = filepath.Dir(gofile)
115 | }
116 |
117 | if len(files) == 0 && gofile != "" {
118 | files = []string{gofile}
119 | } else if len(files) == 0 {
120 | flag.Usage()
121 | os.Exit(1)
122 | }
123 |
124 | for _, fname := range files {
125 | if err := generate(fname); err != nil {
126 | fmt.Fprintln(os.Stderr, err)
127 | os.Exit(1)
128 | }
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/bootstrap/bootstrap.go:
--------------------------------------------------------------------------------
1 | // Package bootstrap implements the bootstrapping logic: generation of a .go file to
2 | // launch the actual generator and launching the generator itself.
3 | //
4 | // The package may be preferred to a command-line utility if generating the serializers
5 | // from golang code is required.
6 | package bootstrap
7 |
8 | import (
9 | "fmt"
10 | "go/format"
11 | "io/ioutil"
12 | "os"
13 | "os/exec"
14 | "path/filepath"
15 | "regexp"
16 | "sort"
17 | )
18 |
19 | const genPackage = "github.com/mailru/easyjson/gen"
20 | const pkgWriter = "github.com/mailru/easyjson/jwriter"
21 | const pkgLexer = "github.com/mailru/easyjson/jlexer"
22 |
23 | var buildFlagsRegexp = regexp.MustCompile("'.+'|\".+\"|\\S+")
24 |
25 | type Generator struct {
26 | PkgPath, PkgName string
27 | Types []string
28 |
29 | NoStdMarshalers bool
30 | SnakeCase bool
31 | LowerCamelCase bool
32 | OmitEmpty bool
33 | DisallowUnknownFields bool
34 | SkipMemberNameUnescaping bool
35 |
36 | OutName string
37 | BuildTags string
38 | GenBuildFlags string
39 |
40 | StubsOnly bool
41 | LeaveTemps bool
42 | NoFormat bool
43 | SimpleBytes bool
44 | }
45 |
46 | // writeStub outputs an initial stub for marshalers/unmarshalers so that the package
47 | // using marshalers/unmarshales compiles correctly for boostrapping code.
48 | func (g *Generator) writeStub() error {
49 | f, err := os.Create(g.OutName)
50 | if err != nil {
51 | return err
52 | }
53 | defer f.Close()
54 |
55 | if g.BuildTags != "" {
56 | fmt.Fprintln(f, "// +build ", g.BuildTags)
57 | fmt.Fprintln(f)
58 | }
59 | fmt.Fprintln(f, "// TEMPORARY AUTOGENERATED FILE: easyjson stub code to make the package")
60 | fmt.Fprintln(f, "// compilable during generation.")
61 | fmt.Fprintln(f)
62 | fmt.Fprintln(f, "package ", g.PkgName)
63 |
64 | if len(g.Types) > 0 {
65 | fmt.Fprintln(f)
66 | fmt.Fprintln(f, "import (")
67 | fmt.Fprintln(f, ` "`+pkgWriter+`"`)
68 | fmt.Fprintln(f, ` "`+pkgLexer+`"`)
69 | fmt.Fprintln(f, ")")
70 | }
71 |
72 | sort.Strings(g.Types)
73 | for _, t := range g.Types {
74 | fmt.Fprintln(f)
75 | if !g.NoStdMarshalers {
76 | fmt.Fprintln(f, "func (", t, ") MarshalJSON() ([]byte, error) { return nil, nil }")
77 | fmt.Fprintln(f, "func (*", t, ") UnmarshalJSON([]byte) error { return nil }")
78 | }
79 |
80 | fmt.Fprintln(f, "func (", t, ") MarshalEasyJSON(w *jwriter.Writer) {}")
81 | fmt.Fprintln(f, "func (*", t, ") UnmarshalEasyJSON(l *jlexer.Lexer) {}")
82 | fmt.Fprintln(f)
83 | fmt.Fprintln(f, "type EasyJSON_exporter_"+t+" *"+t)
84 | }
85 | return nil
86 | }
87 |
88 | // writeMain creates a .go file that launches the generator if 'go run'.
89 | func (g *Generator) writeMain() (path string, err error) {
90 | f, err := ioutil.TempFile(filepath.Dir(g.OutName), "easyjson-bootstrap")
91 | if err != nil {
92 | return "", err
93 | }
94 |
95 | fmt.Fprintln(f, "// +build ignore")
96 | fmt.Fprintln(f)
97 | fmt.Fprintln(f, "// TEMPORARY AUTOGENERATED FILE: easyjson bootstapping code to launch")
98 | fmt.Fprintln(f, "// the actual generator.")
99 | fmt.Fprintln(f)
100 | fmt.Fprintln(f, "package main")
101 | fmt.Fprintln(f)
102 | fmt.Fprintln(f, "import (")
103 | fmt.Fprintln(f, ` "fmt"`)
104 | fmt.Fprintln(f, ` "os"`)
105 | fmt.Fprintln(f)
106 | fmt.Fprintf(f, " %q\n", genPackage)
107 | if len(g.Types) > 0 {
108 | fmt.Fprintln(f)
109 | fmt.Fprintf(f, " pkg %q\n", g.PkgPath)
110 | }
111 | fmt.Fprintln(f, ")")
112 | fmt.Fprintln(f)
113 | fmt.Fprintln(f, "func main() {")
114 | fmt.Fprintf(f, " g := gen.NewGenerator(%q)\n", filepath.Base(g.OutName))
115 | fmt.Fprintf(f, " g.SetPkg(%q, %q)\n", g.PkgName, g.PkgPath)
116 | if g.BuildTags != "" {
117 | fmt.Fprintf(f, " g.SetBuildTags(%q)\n", g.BuildTags)
118 | }
119 | if g.SnakeCase {
120 | fmt.Fprintln(f, " g.UseSnakeCase()")
121 | }
122 | if g.LowerCamelCase {
123 | fmt.Fprintln(f, " g.UseLowerCamelCase()")
124 | }
125 | if g.OmitEmpty {
126 | fmt.Fprintln(f, " g.OmitEmpty()")
127 | }
128 | if g.NoStdMarshalers {
129 | fmt.Fprintln(f, " g.NoStdMarshalers()")
130 | }
131 | if g.DisallowUnknownFields {
132 | fmt.Fprintln(f, " g.DisallowUnknownFields()")
133 | }
134 | if g.SimpleBytes {
135 | fmt.Fprintln(f, " g.SimpleBytes()")
136 | }
137 | if g.SkipMemberNameUnescaping {
138 | fmt.Fprintln(f, " g.SkipMemberNameUnescaping()")
139 | }
140 |
141 | sort.Strings(g.Types)
142 | for _, v := range g.Types {
143 | fmt.Fprintln(f, " g.Add(pkg.EasyJSON_exporter_"+v+"(nil))")
144 | }
145 |
146 | fmt.Fprintln(f, " if err := g.Run(os.Stdout); err != nil {")
147 | fmt.Fprintln(f, " fmt.Fprintln(os.Stderr, err)")
148 | fmt.Fprintln(f, " os.Exit(1)")
149 | fmt.Fprintln(f, " }")
150 | fmt.Fprintln(f, "}")
151 |
152 | src := f.Name()
153 | if err := f.Close(); err != nil {
154 | return src, err
155 | }
156 |
157 | dest := src + ".go"
158 | return dest, os.Rename(src, dest)
159 | }
160 |
161 | func (g *Generator) Run() error {
162 | if err := g.writeStub(); err != nil {
163 | return err
164 | }
165 | if g.StubsOnly {
166 | return nil
167 | }
168 |
169 | path, err := g.writeMain()
170 | if err != nil {
171 | return err
172 | }
173 | if !g.LeaveTemps {
174 | defer os.Remove(path)
175 | }
176 |
177 | f, err := os.Create(g.OutName + ".tmp")
178 | if err != nil {
179 | return err
180 | }
181 | if !g.LeaveTemps {
182 | defer os.Remove(f.Name()) // will not remove after rename
183 | }
184 |
185 | execArgs := []string{"run"}
186 | if g.GenBuildFlags != "" {
187 | buildFlags := buildFlagsRegexp.FindAllString(g.GenBuildFlags, -1)
188 | execArgs = append(execArgs, buildFlags...)
189 | }
190 | if len(g.BuildTags) > 0 {
191 | execArgs = append(execArgs, "-tags", g.BuildTags)
192 | }
193 | execArgs = append(execArgs, filepath.Base(path))
194 | cmd := exec.Command("go", execArgs...)
195 |
196 | cmd.Stdout = f
197 | cmd.Stderr = os.Stderr
198 | cmd.Dir = filepath.Dir(path)
199 | if err = cmd.Run(); err != nil {
200 | return err
201 | }
202 | f.Close()
203 |
204 | // move unformatted file to out path
205 | if g.NoFormat {
206 | return os.Rename(f.Name(), g.OutName)
207 | }
208 |
209 | // format file and write to out path
210 | in, err := ioutil.ReadFile(f.Name())
211 | if err != nil {
212 | return err
213 | }
214 | out, err := format.Source(in)
215 | if err != nil {
216 | return err
217 | }
218 | return ioutil.WriteFile(g.OutName, out, 0644)
219 | }
220 |
--------------------------------------------------------------------------------
/benchmark/codec_test.go:
--------------------------------------------------------------------------------
1 | // +build use_codec
2 |
3 | package benchmark
4 |
5 | import (
6 | "testing"
7 |
8 | "github.com/ugorji/go/codec"
9 | )
10 |
11 | func BenchmarkCodec_Unmarshal_M(b *testing.B) {
12 | var h codec.Handle = new(codec.JsonHandle)
13 | dec := codec.NewDecoderBytes(nil, h)
14 |
15 | b.SetBytes(int64(len(largeStructText)))
16 | for i := 0; i < b.N; i++ {
17 | var s LargeStruct
18 | dec.ResetBytes(largeStructText)
19 | if err := dec.Decode(&s); err != nil {
20 | b.Error(err)
21 | }
22 | }
23 | }
24 |
25 | func BenchmarkCodec_Unmarshal_S(b *testing.B) {
26 | var h codec.Handle = new(codec.JsonHandle)
27 | dec := codec.NewDecoderBytes(nil, h)
28 |
29 | b.SetBytes(int64(len(smallStructText)))
30 | for i := 0; i < b.N; i++ {
31 | var s LargeStruct
32 | dec.ResetBytes(smallStructText)
33 | if err := dec.Decode(&s); err != nil {
34 | b.Error(err)
35 | }
36 | }
37 | }
38 |
39 | func BenchmarkCodec_Marshal_S(b *testing.B) {
40 | var h codec.Handle = new(codec.JsonHandle)
41 |
42 | var out []byte
43 | enc := codec.NewEncoderBytes(&out, h)
44 |
45 | var l int64
46 | for i := 0; i < b.N; i++ {
47 | enc.ResetBytes(&out)
48 | if err := enc.Encode(&smallStructData); err != nil {
49 | b.Error(err)
50 | }
51 | l = int64(len(out))
52 | out = nil
53 | }
54 |
55 | b.SetBytes(l)
56 | }
57 |
58 | func BenchmarkCodec_Marshal_M(b *testing.B) {
59 | var h codec.Handle = new(codec.JsonHandle)
60 |
61 | var out []byte
62 | enc := codec.NewEncoderBytes(&out, h)
63 |
64 | var l int64
65 | for i := 0; i < b.N; i++ {
66 | enc.ResetBytes(&out)
67 | if err := enc.Encode(&largeStructData); err != nil {
68 | b.Error(err)
69 | }
70 | l = int64(len(out))
71 | out = nil
72 | }
73 |
74 | b.SetBytes(l)
75 | }
76 |
77 | func BenchmarkCodec_Marshal_L(b *testing.B) {
78 | var h codec.Handle = new(codec.JsonHandle)
79 |
80 | var out []byte
81 | enc := codec.NewEncoderBytes(&out, h)
82 |
83 | var l int64
84 | for i := 0; i < b.N; i++ {
85 | enc.ResetBytes(&out)
86 | if err := enc.Encode(&xlStructData); err != nil {
87 | b.Error(err)
88 | }
89 | l = int64(len(out))
90 | out = nil
91 | }
92 |
93 | b.SetBytes(l)
94 | }
95 |
96 | func BenchmarkCodec_Marshal_S_Reuse(b *testing.B) {
97 | var h codec.Handle = new(codec.JsonHandle)
98 |
99 | var out []byte
100 | enc := codec.NewEncoderBytes(&out, h)
101 |
102 | var l int64
103 | for i := 0; i < b.N; i++ {
104 | enc.ResetBytes(&out)
105 | if err := enc.Encode(&smallStructData); err != nil {
106 | b.Error(err)
107 | }
108 | l = int64(len(out))
109 | out = out[:0]
110 | }
111 |
112 | b.SetBytes(l)
113 | }
114 |
115 | func BenchmarkCodec_Marshal_M_Reuse(b *testing.B) {
116 | var h codec.Handle = new(codec.JsonHandle)
117 |
118 | var out []byte
119 | enc := codec.NewEncoderBytes(&out, h)
120 |
121 | var l int64
122 | for i := 0; i < b.N; i++ {
123 | enc.ResetBytes(&out)
124 | if err := enc.Encode(&largeStructData); err != nil {
125 | b.Error(err)
126 | }
127 | l = int64(len(out))
128 | out = out[:0]
129 | }
130 |
131 | b.SetBytes(l)
132 | }
133 |
134 | func BenchmarkCodec_Marshal_L_Reuse(b *testing.B) {
135 | var h codec.Handle = new(codec.JsonHandle)
136 |
137 | var out []byte
138 | enc := codec.NewEncoderBytes(&out, h)
139 |
140 | var l int64
141 | for i := 0; i < b.N; i++ {
142 | enc.ResetBytes(&out)
143 | if err := enc.Encode(&xlStructData); err != nil {
144 | b.Error(err)
145 | }
146 | l = int64(len(out))
147 | out = out[:0]
148 | }
149 |
150 | b.SetBytes(l)
151 | }
152 |
153 | func BenchmarkCodec_Marshal_S_Parallel(b *testing.B) {
154 | var l int64
155 |
156 | b.RunParallel(func(pb *testing.PB) {
157 | var out []byte
158 |
159 | var h codec.Handle = new(codec.JsonHandle)
160 | enc := codec.NewEncoderBytes(&out, h)
161 |
162 | for pb.Next() {
163 | enc.ResetBytes(&out)
164 | if err := enc.Encode(&smallStructData); err != nil {
165 | b.Error(err)
166 | }
167 | l = int64(len(out))
168 | out = nil
169 | }
170 | })
171 |
172 | b.SetBytes(l)
173 | }
174 |
175 | func BenchmarkCodec_Marshal_M_Parallel(b *testing.B) {
176 | var l int64
177 |
178 | b.RunParallel(func(pb *testing.PB) {
179 | var h codec.Handle = new(codec.JsonHandle)
180 |
181 | var out []byte
182 | enc := codec.NewEncoderBytes(&out, h)
183 |
184 | for pb.Next() {
185 | enc.ResetBytes(&out)
186 | if err := enc.Encode(&largeStructData); err != nil {
187 | b.Error(err)
188 | }
189 | l = int64(len(out))
190 | out = nil
191 | }
192 | })
193 | b.SetBytes(l)
194 | }
195 |
196 | func BenchmarkCodec_Marshal_L_Parallel(b *testing.B) {
197 | var l int64
198 |
199 | b.RunParallel(func(pb *testing.PB) {
200 | var h codec.Handle = new(codec.JsonHandle)
201 |
202 | var out []byte
203 | enc := codec.NewEncoderBytes(&out, h)
204 |
205 | for pb.Next() {
206 | enc.ResetBytes(&out)
207 | if err := enc.Encode(&xlStructData); err != nil {
208 | b.Error(err)
209 | }
210 | l = int64(len(out))
211 | out = nil
212 | }
213 | })
214 | b.SetBytes(l)
215 | }
216 |
217 | func BenchmarkCodec_Marshal_S_Parallel_Reuse(b *testing.B) {
218 | var l int64
219 |
220 | b.RunParallel(func(pb *testing.PB) {
221 | var out []byte
222 |
223 | var h codec.Handle = new(codec.JsonHandle)
224 | enc := codec.NewEncoderBytes(&out, h)
225 |
226 | for pb.Next() {
227 | enc.ResetBytes(&out)
228 | if err := enc.Encode(&smallStructData); err != nil {
229 | b.Error(err)
230 | }
231 | l = int64(len(out))
232 | out = out[:0]
233 | }
234 | })
235 |
236 | b.SetBytes(l)
237 | }
238 |
239 | func BenchmarkCodec_Marshal_M_Parallel_Reuse(b *testing.B) {
240 | var l int64
241 |
242 | b.RunParallel(func(pb *testing.PB) {
243 | var h codec.Handle = new(codec.JsonHandle)
244 |
245 | var out []byte
246 | enc := codec.NewEncoderBytes(&out, h)
247 |
248 | for pb.Next() {
249 | enc.ResetBytes(&out)
250 | if err := enc.Encode(&largeStructData); err != nil {
251 | b.Error(err)
252 | }
253 | l = int64(len(out))
254 | out = out[:0]
255 | }
256 | })
257 | b.SetBytes(l)
258 | }
259 |
260 | func BenchmarkCodec_Marshal_L_Parallel_Reuse(b *testing.B) {
261 | var l int64
262 |
263 | b.RunParallel(func(pb *testing.PB) {
264 | var h codec.Handle = new(codec.JsonHandle)
265 |
266 | var out []byte
267 | enc := codec.NewEncoderBytes(&out, h)
268 |
269 | for pb.Next() {
270 | enc.ResetBytes(&out)
271 | if err := enc.Encode(&xlStructData); err != nil {
272 | b.Error(err)
273 | }
274 | l = int64(len(out))
275 | out = out[:0]
276 | }
277 | })
278 | b.SetBytes(l)
279 | }
280 |
--------------------------------------------------------------------------------
/benchmark/data.go:
--------------------------------------------------------------------------------
1 | // Package benchmark provides a simple benchmark for easyjson against default serialization and ffjson.
2 | // The data example is taken from https://dev.twitter.com/rest/reference/get/search/tweets
3 | package benchmark
4 |
5 | import (
6 | "io/ioutil"
7 | )
8 |
9 | var largeStructText, _ = ioutil.ReadFile("example.json")
10 | var xlStructData XLStruct
11 |
12 | func init() {
13 | for i := 0; i < 50; i++ {
14 | xlStructData.Data = append(xlStructData.Data, largeStructData)
15 | }
16 | }
17 |
18 | var smallStructText = []byte(`{"hashtags":[{"indices":[5, 10],"text":"some-text"}],"urls":[],"user_mentions":[]}`)
19 | var smallStructData = Entities{
20 | Hashtags: []Hashtag{{Indices: []int{5, 10}, Text: "some-text"}},
21 | Urls: []*string{},
22 | UserMentions: []*string{},
23 | }
24 |
25 | type SearchMetadata struct {
26 | CompletedIn float64 `json:"completed_in"`
27 | Count int `json:"count"`
28 | MaxID int64 `json:"max_id"`
29 | MaxIDStr string `json:"max_id_str"`
30 | NextResults string `json:"next_results"`
31 | Query string `json:"query"`
32 | RefreshURL string `json:"refresh_url"`
33 | SinceID int64 `json:"since_id"`
34 | SinceIDStr string `json:"since_id_str"`
35 | }
36 |
37 | type Hashtag struct {
38 | Indices []int `json:"indices"`
39 | Text string `json:"text"`
40 | }
41 |
42 | //easyjson:json
43 | type Entities struct {
44 | Hashtags []Hashtag `json:"hashtags"`
45 | Urls []*string `json:"urls"`
46 | UserMentions []*string `json:"user_mentions"`
47 | }
48 |
49 | type UserEntityDescription struct {
50 | Urls []*string `json:"urls"`
51 | }
52 |
53 | type URL struct {
54 | ExpandedURL *string `json:"expanded_url"`
55 | Indices []int `json:"indices"`
56 | URL string `json:"url"`
57 | }
58 |
59 | type UserEntityURL struct {
60 | Urls []URL `json:"urls"`
61 | }
62 |
63 | type UserEntities struct {
64 | Description UserEntityDescription `json:"description"`
65 | URL UserEntityURL `json:"url"`
66 | }
67 |
68 | type User struct {
69 | ContributorsEnabled bool `json:"contributors_enabled"`
70 | CreatedAt string `json:"created_at"`
71 | DefaultProfile bool `json:"default_profile"`
72 | DefaultProfileImage bool `json:"default_profile_image"`
73 | Description string `json:"description"`
74 | Entities UserEntities `json:"entities"`
75 | FavouritesCount int `json:"favourites_count"`
76 | FollowRequestSent *string `json:"follow_request_sent"`
77 | FollowersCount int `json:"followers_count"`
78 | Following *string `json:"following"`
79 | FriendsCount int `json:"friends_count"`
80 | GeoEnabled bool `json:"geo_enabled"`
81 | ID int `json:"id"`
82 | IDStr string `json:"id_str"`
83 | IsTranslator bool `json:"is_translator"`
84 | Lang string `json:"lang"`
85 | ListedCount int `json:"listed_count"`
86 | Location string `json:"location"`
87 | Name string `json:"name"`
88 | Notifications *string `json:"notifications"`
89 | ProfileBackgroundColor string `json:"profile_background_color"`
90 | ProfileBackgroundImageURL string `json:"profile_background_image_url"`
91 | ProfileBackgroundImageURLHTTPS string `json:"profile_background_image_url_https"`
92 | ProfileBackgroundTile bool `json:"profile_background_tile"`
93 | ProfileImageURL string `json:"profile_image_url"`
94 | ProfileImageURLHTTPS string `json:"profile_image_url_https"`
95 | ProfileLinkColor string `json:"profile_link_color"`
96 | ProfileSidebarBorderColor string `json:"profile_sidebar_border_color"`
97 | ProfileSidebarFillColor string `json:"profile_sidebar_fill_color"`
98 | ProfileTextColor string `json:"profile_text_color"`
99 | ProfileUseBackgroundImage bool `json:"profile_use_background_image"`
100 | Protected bool `json:"protected"`
101 | ScreenName string `json:"screen_name"`
102 | ShowAllInlineMedia bool `json:"show_all_inline_media"`
103 | StatusesCount int `json:"statuses_count"`
104 | TimeZone string `json:"time_zone"`
105 | URL *string `json:"url"`
106 | UtcOffset int `json:"utc_offset"`
107 | Verified bool `json:"verified"`
108 | }
109 |
110 | type StatusMetadata struct {
111 | IsoLanguageCode string `json:"iso_language_code"`
112 | ResultType string `json:"result_type"`
113 | }
114 |
115 | type Status struct {
116 | Contributors *string `json:"contributors"`
117 | Coordinates *string `json:"coordinates"`
118 | CreatedAt string `json:"created_at"`
119 | Entities Entities `json:"entities"`
120 | Favorited bool `json:"favorited"`
121 | Geo *string `json:"geo"`
122 | ID int64 `json:"id"`
123 | IDStr string `json:"id_str"`
124 | InReplyToScreenName *string `json:"in_reply_to_screen_name"`
125 | InReplyToStatusID *string `json:"in_reply_to_status_id"`
126 | InReplyToStatusIDStr *string `json:"in_reply_to_status_id_str"`
127 | InReplyToUserID *string `json:"in_reply_to_user_id"`
128 | InReplyToUserIDStr *string `json:"in_reply_to_user_id_str"`
129 | Metadata StatusMetadata `json:"metadata"`
130 | Place *string `json:"place"`
131 | RetweetCount int `json:"retweet_count"`
132 | Retweeted bool `json:"retweeted"`
133 | Source string `json:"source"`
134 | Text string `json:"text"`
135 | Truncated bool `json:"truncated"`
136 | User User `json:"user"`
137 | }
138 |
139 | //easyjson:json
140 | type LargeStruct struct {
141 | SearchMetadata SearchMetadata `json:"search_metadata"`
142 | Statuses []Status `json:"statuses"`
143 | }
144 |
145 | //easyjson:json
146 | type XLStruct struct {
147 | Data []LargeStruct
148 | }
149 |
--------------------------------------------------------------------------------
/buffer/pool.go:
--------------------------------------------------------------------------------
1 | // Package buffer implements a buffer for serialization, consisting of a chain of []byte-s to
2 | // reduce copying and to allow reuse of individual chunks.
3 | package buffer
4 |
5 | import (
6 | "io"
7 | "net"
8 | "sync"
9 | )
10 |
11 | // PoolConfig contains configuration for the allocation and reuse strategy.
12 | type PoolConfig struct {
13 | StartSize int // Minimum chunk size that is allocated.
14 | PooledSize int // Minimum chunk size that is reused, reusing chunks too small will result in overhead.
15 | MaxSize int // Maximum chunk size that will be allocated.
16 | }
17 |
18 | var config = PoolConfig{
19 | StartSize: 128,
20 | PooledSize: 512,
21 | MaxSize: 32768,
22 | }
23 |
24 | // Reuse pool: chunk size -> pool.
25 | var buffers = map[int]*sync.Pool{}
26 |
27 | func initBuffers() {
28 | for l := config.PooledSize; l <= config.MaxSize; l *= 2 {
29 | buffers[l] = new(sync.Pool)
30 | }
31 | }
32 |
33 | func init() {
34 | initBuffers()
35 | }
36 |
37 | // Init sets up a non-default pooling and allocation strategy. Should be run before serialization is done.
38 | func Init(cfg PoolConfig) {
39 | config = cfg
40 | initBuffers()
41 | }
42 |
43 | // putBuf puts a chunk to reuse pool if it can be reused.
44 | func putBuf(buf []byte) {
45 | size := cap(buf)
46 | if size < config.PooledSize {
47 | return
48 | }
49 | if c := buffers[size]; c != nil {
50 | c.Put(buf[:0])
51 | }
52 | }
53 |
54 | // getBuf gets a chunk from reuse pool or creates a new one if reuse failed.
55 | func getBuf(size int) []byte {
56 | if size >= config.PooledSize {
57 | if c := buffers[size]; c != nil {
58 | v := c.Get()
59 | if v != nil {
60 | return v.([]byte)
61 | }
62 | }
63 | }
64 | return make([]byte, 0, size)
65 | }
66 |
67 | // Buffer is a buffer optimized for serialization without extra copying.
68 | type Buffer struct {
69 |
70 | // Buf is the current chunk that can be used for serialization.
71 | Buf []byte
72 |
73 | toPool []byte
74 | bufs [][]byte
75 | }
76 |
77 | // EnsureSpace makes sure that the current chunk contains at least s free bytes,
78 | // possibly creating a new chunk.
79 | func (b *Buffer) EnsureSpace(s int) {
80 | if cap(b.Buf)-len(b.Buf) < s {
81 | b.ensureSpaceSlow(s)
82 | }
83 | }
84 |
85 | func (b *Buffer) ensureSpaceSlow(s int) {
86 | l := len(b.Buf)
87 | if l > 0 {
88 | if cap(b.toPool) != cap(b.Buf) {
89 | // Chunk was reallocated, toPool can be pooled.
90 | putBuf(b.toPool)
91 | }
92 | if cap(b.bufs) == 0 {
93 | b.bufs = make([][]byte, 0, 8)
94 | }
95 | b.bufs = append(b.bufs, b.Buf)
96 | l = cap(b.toPool) * 2
97 | } else {
98 | l = config.StartSize
99 | }
100 |
101 | if l > config.MaxSize {
102 | l = config.MaxSize
103 | }
104 | b.Buf = getBuf(l)
105 | b.toPool = b.Buf
106 | }
107 |
108 | // AppendByte appends a single byte to buffer.
109 | func (b *Buffer) AppendByte(data byte) {
110 | b.EnsureSpace(1)
111 | b.Buf = append(b.Buf, data)
112 | }
113 |
114 | // AppendBytes appends a byte slice to buffer.
115 | func (b *Buffer) AppendBytes(data []byte) {
116 | if len(data) <= cap(b.Buf)-len(b.Buf) {
117 | b.Buf = append(b.Buf, data...) // fast path
118 | } else {
119 | b.appendBytesSlow(data)
120 | }
121 | }
122 |
123 | func (b *Buffer) appendBytesSlow(data []byte) {
124 | for len(data) > 0 {
125 | b.EnsureSpace(1)
126 |
127 | sz := cap(b.Buf) - len(b.Buf)
128 | if sz > len(data) {
129 | sz = len(data)
130 | }
131 |
132 | b.Buf = append(b.Buf, data[:sz]...)
133 | data = data[sz:]
134 | }
135 | }
136 |
137 | // AppendString appends a string to buffer.
138 | func (b *Buffer) AppendString(data string) {
139 | if len(data) <= cap(b.Buf)-len(b.Buf) {
140 | b.Buf = append(b.Buf, data...) // fast path
141 | } else {
142 | b.appendStringSlow(data)
143 | }
144 | }
145 |
146 | func (b *Buffer) appendStringSlow(data string) {
147 | for len(data) > 0 {
148 | b.EnsureSpace(1)
149 |
150 | sz := cap(b.Buf) - len(b.Buf)
151 | if sz > len(data) {
152 | sz = len(data)
153 | }
154 |
155 | b.Buf = append(b.Buf, data[:sz]...)
156 | data = data[sz:]
157 | }
158 | }
159 |
160 | // Size computes the size of a buffer by adding sizes of every chunk.
161 | func (b *Buffer) Size() int {
162 | size := len(b.Buf)
163 | for _, buf := range b.bufs {
164 | size += len(buf)
165 | }
166 | return size
167 | }
168 |
169 | // DumpTo outputs the contents of a buffer to a writer and resets the buffer.
170 | func (b *Buffer) DumpTo(w io.Writer) (written int, err error) {
171 | bufs := net.Buffers(b.bufs)
172 | if len(b.Buf) > 0 {
173 | bufs = append(bufs, b.Buf)
174 | }
175 | n, err := bufs.WriteTo(w)
176 |
177 | for _, buf := range b.bufs {
178 | putBuf(buf)
179 | }
180 | putBuf(b.toPool)
181 |
182 | b.bufs = nil
183 | b.Buf = nil
184 | b.toPool = nil
185 |
186 | return int(n), err
187 | }
188 |
189 | // BuildBytes creates a single byte slice with all the contents of the buffer. Data is
190 | // copied if it does not fit in a single chunk. You can optionally provide one byte
191 | // slice as argument that it will try to reuse.
192 | func (b *Buffer) BuildBytes(reuse ...[]byte) []byte {
193 | if len(b.bufs) == 0 {
194 | ret := b.Buf
195 | b.toPool = nil
196 | b.Buf = nil
197 | return ret
198 | }
199 |
200 | var ret []byte
201 | size := b.Size()
202 |
203 | // If we got a buffer as argument and it is big enough, reuse it.
204 | if len(reuse) == 1 && cap(reuse[0]) >= size {
205 | ret = reuse[0][:0]
206 | } else {
207 | ret = make([]byte, 0, size)
208 | }
209 | for _, buf := range b.bufs {
210 | ret = append(ret, buf...)
211 | putBuf(buf)
212 | }
213 |
214 | ret = append(ret, b.Buf...)
215 | putBuf(b.toPool)
216 |
217 | b.bufs = nil
218 | b.toPool = nil
219 | b.Buf = nil
220 |
221 | return ret
222 | }
223 |
224 | type readCloser struct {
225 | offset int
226 | bufs [][]byte
227 | }
228 |
229 | func (r *readCloser) Read(p []byte) (n int, err error) {
230 | for _, buf := range r.bufs {
231 | // Copy as much as we can.
232 | x := copy(p[n:], buf[r.offset:])
233 | n += x // Increment how much we filled.
234 |
235 | // Did we empty the whole buffer?
236 | if r.offset+x == len(buf) {
237 | // On to the next buffer.
238 | r.offset = 0
239 | r.bufs = r.bufs[1:]
240 |
241 | // We can release this buffer.
242 | putBuf(buf)
243 | } else {
244 | r.offset += x
245 | }
246 |
247 | if n == len(p) {
248 | break
249 | }
250 | }
251 | // No buffers left or nothing read?
252 | if len(r.bufs) == 0 {
253 | err = io.EOF
254 | }
255 | return
256 | }
257 |
258 | func (r *readCloser) Close() error {
259 | // Release all remaining buffers.
260 | for _, buf := range r.bufs {
261 | putBuf(buf)
262 | }
263 | // In case Close gets called multiple times.
264 | r.bufs = nil
265 |
266 | return nil
267 | }
268 |
269 | // ReadCloser creates an io.ReadCloser with all the contents of the buffer.
270 | func (b *Buffer) ReadCloser() io.ReadCloser {
271 | ret := &readCloser{0, append(b.bufs, b.Buf)}
272 |
273 | b.bufs = nil
274 | b.toPool = nil
275 | b.Buf = nil
276 |
277 | return ret
278 | }
279 |
--------------------------------------------------------------------------------
/tests/errors_test.go:
--------------------------------------------------------------------------------
1 | package tests
2 |
3 | import (
4 | "math"
5 | "testing"
6 |
7 | "github.com/mailru/easyjson/jlexer"
8 | )
9 |
10 | func TestMultipleErrorsInt(t *testing.T) {
11 | for i, test := range []struct {
12 | Data []byte
13 | Offsets []int
14 | }{
15 | {
16 | Data: []byte(`[1, 2, 3, "4", "5"]`),
17 | Offsets: []int{10, 15},
18 | },
19 | {
20 | Data: []byte(`[1, {"2":"3"}, 3, "4"]`),
21 | Offsets: []int{4, 18},
22 | },
23 | {
24 | Data: []byte(`[1, "2", "3", "4", "5", "6"]`),
25 | Offsets: []int{4, 9, 14, 19, 24},
26 | },
27 | {
28 | Data: []byte(`[1, 2, 3, 4, "5"]`),
29 | Offsets: []int{13},
30 | },
31 | {
32 | Data: []byte(`[{"1": "2"}]`),
33 | Offsets: []int{1},
34 | },
35 | } {
36 | l := jlexer.Lexer{
37 | Data: test.Data,
38 | UseMultipleErrors: true,
39 | }
40 |
41 | var v ErrorIntSlice
42 |
43 | v.UnmarshalEasyJSON(&l)
44 |
45 | errors := l.GetNonFatalErrors()
46 |
47 | if len(errors) != len(test.Offsets) {
48 | t.Errorf("[%d] TestMultipleErrorsInt(): errornum: want: %d, got %d", i, len(test.Offsets), len(errors))
49 | return
50 | }
51 |
52 | for ii, e := range errors {
53 | if e.Offset != test.Offsets[ii] {
54 | t.Errorf("[%d] TestMultipleErrorsInt(): offset[%d]: want %d, got %d", i, ii, test.Offsets[ii], e.Offset)
55 | }
56 | }
57 | }
58 | }
59 |
60 | func TestMultipleErrorsBool(t *testing.T) {
61 | for i, test := range []struct {
62 | Data []byte
63 | Offsets []int
64 | }{
65 | {
66 | Data: []byte(`[true, false, true, false]`),
67 | },
68 | {
69 | Data: []byte(`["test", "value", "lol", "1"]`),
70 | Offsets: []int{1, 9, 18, 25},
71 | },
72 | {
73 | Data: []byte(`[true, 42, {"a":"b", "c":"d"}, false]`),
74 | Offsets: []int{7, 11},
75 | },
76 | } {
77 | l := jlexer.Lexer{
78 | Data: test.Data,
79 | UseMultipleErrors: true,
80 | }
81 |
82 | var v ErrorBoolSlice
83 | v.UnmarshalEasyJSON(&l)
84 |
85 | errors := l.GetNonFatalErrors()
86 |
87 | if len(errors) != len(test.Offsets) {
88 | t.Errorf("[%d] TestMultipleErrorsBool(): errornum: want: %d, got %d", i, len(test.Offsets), len(errors))
89 | return
90 | }
91 | for ii, e := range errors {
92 | if e.Offset != test.Offsets[ii] {
93 | t.Errorf("[%d] TestMultipleErrorsBool(): offset[%d]: want %d, got %d", i, ii, test.Offsets[ii], e.Offset)
94 | }
95 | }
96 | }
97 | }
98 |
99 | func TestMultipleErrorsUint(t *testing.T) {
100 | for i, test := range []struct {
101 | Data []byte
102 | Offsets []int
103 | }{
104 | {
105 | Data: []byte(`[42, 42, 42]`),
106 | },
107 | {
108 | Data: []byte(`[17, "42", 32]`),
109 | Offsets: []int{5},
110 | },
111 | {
112 | Data: []byte(`["zz", "zz"]`),
113 | Offsets: []int{1, 7},
114 | },
115 | {
116 | Data: []byte(`[{}, 42]`),
117 | Offsets: []int{1},
118 | },
119 | } {
120 | l := jlexer.Lexer{
121 | Data: test.Data,
122 | UseMultipleErrors: true,
123 | }
124 |
125 | var v ErrorUintSlice
126 | v.UnmarshalEasyJSON(&l)
127 |
128 | errors := l.GetNonFatalErrors()
129 |
130 | if len(errors) != len(test.Offsets) {
131 | t.Errorf("[%d] TestMultipleErrorsUint(): errornum: want: %d, got %d", i, len(test.Offsets), len(errors))
132 | return
133 | }
134 | for ii, e := range errors {
135 | if e.Offset != test.Offsets[ii] {
136 | t.Errorf("[%d] TestMultipleErrorsUint(): offset[%d]: want %d, got %d", i, ii, test.Offsets[ii], e.Offset)
137 | }
138 | }
139 | }
140 | }
141 |
142 | func TestMultipleErrorsStruct(t *testing.T) {
143 | for i, test := range []struct {
144 | Data []byte
145 | Offsets []int
146 | }{
147 | {
148 | Data: []byte(`{"string": "test", "slice":[42, 42, 42], "int_slice":[1, 2, 3]}`),
149 | },
150 | {
151 | Data: []byte(`{"string": {"test": "test"}, "slice":[42, 42, 42], "int_slice":["1", 2, 3]}`),
152 | Offsets: []int{11, 64},
153 | },
154 | {
155 | Data: []byte(`{"slice": [42, 42], "string": {"test": "test"}, "int_slice":["1", "2", 3]}`),
156 | Offsets: []int{30, 61, 66},
157 | },
158 | {
159 | Data: []byte(`{"string": "test", "slice": {}}`),
160 | Offsets: []int{28},
161 | },
162 | {
163 | Data: []byte(`{"slice":5, "string" : "test"}`),
164 | Offsets: []int{9},
165 | },
166 | {
167 | Data: []byte(`{"slice" : "test", "string" : "test"}`),
168 | Offsets: []int{11},
169 | },
170 | {
171 | Data: []byte(`{"slice": "", "string" : {}, "int":{}}`),
172 | Offsets: []int{10, 25, 35},
173 | },
174 | } {
175 | l := jlexer.Lexer{
176 | Data: test.Data,
177 | UseMultipleErrors: true,
178 | }
179 | var v ErrorStruct
180 | v.UnmarshalEasyJSON(&l)
181 |
182 | errors := l.GetNonFatalErrors()
183 |
184 | if len(errors) != len(test.Offsets) {
185 | t.Errorf("[%d] TestMultipleErrorsStruct(): errornum: want: %d, got %d", i, len(test.Offsets), len(errors))
186 | return
187 | }
188 | for ii, e := range errors {
189 | if e.Offset != test.Offsets[ii] {
190 | t.Errorf("[%d] TestMultipleErrorsStruct(): offset[%d]: want %d, got %d", i, ii, test.Offsets[ii], e.Offset)
191 | }
192 | }
193 | }
194 | }
195 |
196 | func TestMultipleErrorsNestedStruct(t *testing.T) {
197 | for i, test := range []struct {
198 | Data []byte
199 | Offsets []int
200 | }{
201 | {
202 | Data: []byte(`{"error_struct":{}}`),
203 | },
204 | {
205 | Data: []byte(`{"error_struct":5}`),
206 | Offsets: []int{16},
207 | },
208 | {
209 | Data: []byte(`{"error_struct":[]}`),
210 | Offsets: []int{16},
211 | },
212 | {
213 | Data: []byte(`{"error_struct":{"int":{}}}`),
214 | Offsets: []int{23},
215 | },
216 | {
217 | Data: []byte(`{"error_struct":{"int_slice":{}}, "int":4}`),
218 | Offsets: []int{29},
219 | },
220 | {
221 | Data: []byte(`{"error_struct":{"int_slice":["1", 2, "3"]}, "int":[]}`),
222 | Offsets: []int{30, 38, 51},
223 | },
224 | } {
225 | l := jlexer.Lexer{
226 | Data: test.Data,
227 | UseMultipleErrors: true,
228 | }
229 | var v ErrorNestedStruct
230 | v.UnmarshalEasyJSON(&l)
231 |
232 | errors := l.GetNonFatalErrors()
233 |
234 | if len(errors) != len(test.Offsets) {
235 | t.Errorf("[%d] TestMultipleErrorsNestedStruct(): errornum: want: %d, got %d", i, len(test.Offsets), len(errors))
236 | return
237 | }
238 | for ii, e := range errors {
239 | if e.Offset != test.Offsets[ii] {
240 | t.Errorf("[%d] TestMultipleErrorsNestedStruct(): offset[%d]: want %d, got %d", i, ii, test.Offsets[ii], e.Offset)
241 | }
242 | }
243 | }
244 | }
245 |
246 | func TestMultipleErrorsIntMap(t *testing.T) {
247 | for i, test := range []struct {
248 | Data []byte
249 | Offsets []int
250 | }{
251 | {
252 | Data: []byte(`{"a":"NumErr"}`),
253 | Offsets: []int{1},
254 | },
255 | {
256 | Data: []byte(`{"":"ErrSyntax"}`),
257 | Offsets: []int{1},
258 | },
259 | {
260 | Data: []byte(`{"a":"NumErr","33147483647":"ErrRange","-1":"ErrRange"}`),
261 | Offsets: []int{1, 14, 39},
262 | },
263 | } {
264 | l := jlexer.Lexer{
265 | Data: test.Data,
266 | UseMultipleErrors: true,
267 | }
268 |
269 | var v ErrorIntMap
270 |
271 | v.UnmarshalEasyJSON(&l)
272 |
273 | errors := l.GetNonFatalErrors()
274 |
275 | if len(errors) != len(test.Offsets) {
276 | t.Errorf("[%d] TestMultipleErrorsInt(): errornum: want: %d, got %d", i, len(test.Offsets), len(errors))
277 | return
278 | }
279 |
280 | for ii, e := range errors {
281 | if e.Offset != test.Offsets[ii] {
282 | t.Errorf("[%d] TestMultipleErrorsInt(): offset[%d]: want %d, got %d", i, ii, test.Offsets[ii], e.Offset)
283 | }
284 | }
285 | }
286 | }
287 |
288 | func TestUnsupportedFloatValues(t *testing.T) {
289 | for i, test := range []struct {
290 | Value ErrorFloatTypes
291 | ExpectedErr string
292 | }{
293 | {
294 | Value: ErrorFloatTypes{Float64: math.NaN()},
295 | ExpectedErr: "json: unsupported value: NaN",
296 | },
297 | {
298 | Value: ErrorFloatTypes{Float64: math.Inf(1)},
299 | ExpectedErr: "json: unsupported value: +Inf",
300 | },
301 | {
302 | Value: ErrorFloatTypes{Float64: math.Inf(-1)},
303 | ExpectedErr: "json: unsupported value: -Inf",
304 | },
305 | {
306 | Value: ErrorFloatTypes{Float32: float32(math.NaN())},
307 | ExpectedErr: "json: unsupported value: NaN",
308 | },
309 | {
310 | Value: ErrorFloatTypes{Float32: float32(math.Inf(1))},
311 | ExpectedErr: "json: unsupported value: +Inf",
312 | },
313 | {
314 | Value: ErrorFloatTypes{Float32: float32(math.Inf(-1))},
315 | ExpectedErr: "json: unsupported value: -Inf",
316 | },
317 | } {
318 | _, err := test.Value.MarshalJSON()
319 | if err == nil || err.Error() != test.ExpectedErr {
320 | t.Errorf("[%d] TestUnsupportedFloatValues(): error: want %s, got %v", i, test.ExpectedErr, err)
321 | }
322 | }
323 | }
324 |
--------------------------------------------------------------------------------
/tests/basic_test.go:
--------------------------------------------------------------------------------
1 | package tests
2 |
3 | import (
4 | "bytes"
5 | "encoding/json"
6 | "net/http/httptest"
7 | "reflect"
8 | "testing"
9 |
10 | "github.com/mailru/easyjson"
11 | "github.com/mailru/easyjson/jwriter"
12 | )
13 |
14 | type testType interface {
15 | json.Marshaler
16 | json.Unmarshaler
17 | }
18 |
19 | var testCases = []struct {
20 | Decoded testType
21 | Encoded string
22 | }{
23 | {&primitiveTypesValue, primitiveTypesString},
24 | {&namedPrimitiveTypesValue, namedPrimitiveTypesString},
25 | {&structsValue, structsString},
26 | {&omitEmptyValue, omitEmptyString},
27 | {&snakeStructValue, snakeStructString},
28 | {&omitEmptyDefaultValue, omitEmptyDefaultString},
29 | {&optsValue, optsString},
30 | {&rawValue, rawString},
31 | {&stdMarshalerValue, stdMarshalerString},
32 | {&userMarshalerValue, userMarshalerString},
33 | {&unexportedStructValue, unexportedStructString},
34 | {&excludedFieldValue, excludedFieldString},
35 | {&sliceValue, sliceString},
36 | {&arrayValue, arrayString},
37 | {&mapsValue, mapsString},
38 | {&deepNestValue, deepNestString},
39 | {&IntsValue, IntsString},
40 | {&mapStringStringValue, mapStringStringString},
41 | {&namedTypeValue, namedTypeValueString},
42 | {&customMapKeyTypeValue, customMapKeyTypeValueString},
43 | {&embeddedTypeValue, embeddedTypeValueString},
44 | {&mapMyIntStringValue, mapMyIntStringValueString},
45 | {&mapIntStringValue, mapIntStringValueString},
46 | {&mapInt32StringValue, mapInt32StringValueString},
47 | {&mapInt64StringValue, mapInt64StringValueString},
48 | {&mapUintStringValue, mapUintStringValueString},
49 | {&mapUint32StringValue, mapUint32StringValueString},
50 | {&mapUint64StringValue, mapUint64StringValueString},
51 | {&mapUintptrStringValue, mapUintptrStringValueString},
52 | {&intKeyedMapStructValue, intKeyedMapStructValueString},
53 | {&intArrayStructValue, intArrayStructValueString},
54 | {&myUInt8SliceValue, myUInt8SliceString},
55 | {&myUInt8ArrayValue, myUInt8ArrayString},
56 | {&mapWithEncodingMarshaler, mapWithEncodingMarshalerString},
57 | {&myGenDeclaredValue, myGenDeclaredString},
58 | {&myGenDeclaredWithCommentValue, myGenDeclaredWithCommentString},
59 | {&myTypeDeclaredValue, myTypeDeclaredString},
60 | {&myTypeNotSkippedValue, myTypeNotSkippedString},
61 | {&intern, internString},
62 | }
63 |
64 | func TestMarshal(t *testing.T) {
65 | for i, test := range testCases {
66 | data, err := test.Decoded.MarshalJSON()
67 | if err != nil {
68 | t.Errorf("[%d, %T] MarshalJSON() error: %v", i, test.Decoded, err)
69 | }
70 |
71 | got := string(data)
72 | if got != test.Encoded {
73 | t.Errorf("[%d, %T] MarshalJSON(): got \n%v\n\t\t want \n%v", i, test.Decoded, got, test.Encoded)
74 | }
75 | }
76 | }
77 |
78 | func TestUnmarshal(t *testing.T) {
79 | for i, test := range testCases {
80 | v1 := reflect.New(reflect.TypeOf(test.Decoded).Elem()).Interface()
81 | v := v1.(testType)
82 |
83 | err := v.UnmarshalJSON([]byte(test.Encoded))
84 | if err != nil {
85 | t.Errorf("[%d, %T] UnmarshalJSON() error: %v", i, test.Decoded, err)
86 | }
87 |
88 | if !reflect.DeepEqual(v, test.Decoded) {
89 | t.Errorf("[%d, %T] UnmarshalJSON(): got \n%+v\n\t\t want \n%+v", i, test.Decoded, v, test.Decoded)
90 | }
91 | }
92 | }
93 |
94 | func TestRawMessageSTD(t *testing.T) {
95 | type T struct {
96 | F easyjson.RawMessage
97 | Fnil easyjson.RawMessage
98 | }
99 |
100 | val := T{F: easyjson.RawMessage([]byte(`"test"`))}
101 | str := `{"F":"test","Fnil":null}`
102 |
103 | data, err := json.Marshal(val)
104 | if err != nil {
105 | t.Errorf("json.Marshal() error: %v", err)
106 | }
107 | got := string(data)
108 | if got != str {
109 | t.Errorf("json.Marshal() = %v; want %v", got, str)
110 | }
111 |
112 | wantV := T{F: easyjson.RawMessage([]byte(`"test"`)), Fnil: easyjson.RawMessage([]byte("null"))}
113 | var gotV T
114 |
115 | err = json.Unmarshal([]byte(str), &gotV)
116 | if err != nil {
117 | t.Errorf("json.Unmarshal() error: %v", err)
118 | }
119 | if !reflect.DeepEqual(gotV, wantV) {
120 | t.Errorf("json.Unmarshal() = %v; want %v", gotV, wantV)
121 | }
122 | }
123 |
124 | func TestParseNull(t *testing.T) {
125 | var got, want SubStruct
126 | if err := easyjson.Unmarshal([]byte("null"), &got); err != nil {
127 | t.Errorf("Unmarshal() error: %v", err)
128 | }
129 |
130 | if !reflect.DeepEqual(got, want) {
131 | t.Errorf("Unmarshal() = %+v; want %+v", got, want)
132 | }
133 | }
134 |
135 | var testSpecialCases = []struct {
136 | EncodedString string
137 | Value string
138 | }{
139 | {`"Username \u003cuser@example.com\u003e"`, `Username `},
140 | {`"Username\ufffd"`, "Username\xc5"},
141 | {`"тестzтест"`, "тестzтест"},
142 | {`"тест\ufffdтест"`, "тест\xc5тест"},
143 | {`"绿茶"`, "绿茶"},
144 | {`"绿\ufffd茶"`, "绿\xc5茶"},
145 | {`"тест\u2028"`, "тест\xE2\x80\xA8"},
146 | {`"\\\r\n\t\""`, "\\\r\n\t\""},
147 | {`"text\\\""`, "text\\\""},
148 | {`"ü"`, "ü"},
149 | }
150 |
151 | func TestSpecialCases(t *testing.T) {
152 | for i, test := range testSpecialCases {
153 | w := jwriter.Writer{}
154 | w.String(test.Value)
155 | got := string(w.Buffer.BuildBytes())
156 | if got != test.EncodedString {
157 | t.Errorf("[%d] Encoded() = %+v; want %+v", i, got, test.EncodedString)
158 | }
159 | }
160 | }
161 |
162 | func TestOverflowArray(t *testing.T) {
163 | var a Arrays
164 | err := easyjson.Unmarshal([]byte(arrayOverflowString), &a)
165 | if err != nil {
166 | t.Error(err)
167 | }
168 | if a != arrayValue {
169 | t.Errorf("Unmarshal(%v) = %+v; want %+v", arrayOverflowString, a, arrayValue)
170 | }
171 | }
172 |
173 | func TestUnderflowArray(t *testing.T) {
174 | var a Arrays
175 | err := easyjson.Unmarshal([]byte(arrayUnderflowString), &a)
176 | if err != nil {
177 | t.Error(err)
178 | }
179 | if a != arrayUnderflowValue {
180 | t.Errorf("Unmarshal(%v) = %+v; want %+v", arrayUnderflowString, a, arrayUnderflowValue)
181 | }
182 | }
183 |
184 | func TestEncodingFlags(t *testing.T) {
185 | for i, test := range []struct {
186 | Flags jwriter.Flags
187 | In easyjson.Marshaler
188 | Want string
189 | }{
190 | {0, EncodingFlagsTestMap{}, `{"F":null}`},
191 | {0, EncodingFlagsTestSlice{}, `{"F":null}`},
192 | {jwriter.NilMapAsEmpty, EncodingFlagsTestMap{}, `{"F":{}}`},
193 | {jwriter.NilSliceAsEmpty, EncodingFlagsTestSlice{}, `{"F":[]}`},
194 | } {
195 | w := &jwriter.Writer{Flags: test.Flags}
196 | test.In.MarshalEasyJSON(w)
197 |
198 | data, err := w.BuildBytes()
199 | if err != nil {
200 | t.Errorf("[%v] easyjson.Marshal(%+v) error: %v", i, test.In, err)
201 | }
202 |
203 | v := string(data)
204 | if v != test.Want {
205 | t.Errorf("[%v] easyjson.Marshal(%+v) = %v; want %v", i, test.In, v, test.Want)
206 | }
207 | }
208 | }
209 |
210 | func TestNestedEasyJsonMarshal(t *testing.T) {
211 | n := map[string]*NestedEasyMarshaler{
212 | "Value": {},
213 | "Slice1": {},
214 | "Slice2": {},
215 | "Map1": {},
216 | "Map2": {},
217 | }
218 |
219 | ni := NestedInterfaces{
220 | Value: n["Value"],
221 | Slice: []interface{}{n["Slice1"], n["Slice2"]},
222 | Map: map[string]interface{}{"1": n["Map1"], "2": n["Map2"]},
223 | }
224 | easyjson.Marshal(ni)
225 |
226 | for k, v := range n {
227 | if !v.EasilyMarshaled {
228 | t.Errorf("Nested interface %s wasn't easily marshaled", k)
229 | }
230 | }
231 | }
232 |
233 | func TestNestedMarshaler(t *testing.T) {
234 | s := NestedMarshaler{
235 | Value: &StructWithMarshaler{
236 | Value: 5,
237 | },
238 | Value2: 10,
239 | }
240 |
241 | data, err := s.MarshalJSON()
242 | if err != nil {
243 | t.Errorf("Can't marshal NestedMarshaler: %s", err)
244 | }
245 |
246 | s2 := NestedMarshaler {
247 | Value: &StructWithMarshaler{},
248 | }
249 |
250 | err = s2.UnmarshalJSON(data)
251 | if err != nil {
252 | t.Errorf("Can't unmarshal NestedMarshaler: %s", err)
253 | }
254 |
255 | if !reflect.DeepEqual(s2, s) {
256 | t.Errorf("easyjson.Unmarshal() = %#v; want %#v", s2, s)
257 | }
258 | }
259 |
260 | func TestUnmarshalStructWithEmbeddedPtrStruct(t *testing.T) {
261 | var s = StructWithInterface{Field2: &EmbeddedStruct{}}
262 | var err error
263 | err = easyjson.Unmarshal([]byte(structWithInterfaceString), &s)
264 | if err != nil {
265 | t.Errorf("easyjson.Unmarshal() error: %v", err)
266 | }
267 | if !reflect.DeepEqual(s, structWithInterfaceValueFilled) {
268 | t.Errorf("easyjson.Unmarshal() = %#v; want %#v", s, structWithInterfaceValueFilled)
269 | }
270 | }
271 |
272 | func TestDisallowUnknown(t *testing.T) {
273 | var d DisallowUnknown
274 | err := easyjson.Unmarshal([]byte(disallowUnknownString), &d)
275 | if err == nil {
276 | t.Error("want error, got nil")
277 | }
278 | }
279 |
280 | var testNotGeneratedTypeCases = []interface{}{
281 | TypeNotDeclared{},
282 | TypeSkipped{},
283 | }
284 |
285 | func TestMethodsNoGenerated(t *testing.T) {
286 | var ok bool
287 | for i, instance := range testNotGeneratedTypeCases {
288 | _, ok = instance.(json.Marshaler)
289 | if ok {
290 | t.Errorf("[%d, %T] Unexpected MarshalJSON()", i, instance)
291 | }
292 |
293 | _, ok = instance.(json.Unmarshaler)
294 | if ok {
295 | t.Errorf("[%d, %T] Unexpected Unmarshaler()", i, instance)
296 | }
297 | }
298 | }
299 |
300 | func TestNil(t *testing.T) {
301 | var p *PrimitiveTypes
302 |
303 | data, err := easyjson.Marshal(p)
304 | if err != nil {
305 | t.Errorf("easyjson.Marshal() error: %v", err)
306 | }
307 | if string(data) != "null" {
308 | t.Errorf("Wanted null, got %q", string(data))
309 | }
310 |
311 | var b bytes.Buffer
312 | if n, err := easyjson.MarshalToWriter(p, &b); err != nil || n != 4 {
313 | t.Errorf("easyjson.MarshalToWriter() error: %v, written %d", err, n)
314 | }
315 |
316 | if s := b.String(); s != "null" {
317 | t.Errorf("Wanted null, got %q", s)
318 | }
319 |
320 | w := httptest.NewRecorder()
321 | started, written, err := easyjson.MarshalToHTTPResponseWriter(p, w)
322 | if !started || written != 4 || err != nil {
323 | t.Errorf("easyjson.MarshalToHTTPResponseWriter() error: %v, written %d, started %t",
324 | err, written, started)
325 | }
326 |
327 | if s := w.Body.String(); s != "null" {
328 | t.Errorf("Wanted null, got %q", s)
329 | }
330 | }
331 |
332 | func TestUnmarshalNull(t *testing.T) {
333 | p := PrimitiveTypes{
334 | String: str,
335 | Ptr: &str,
336 | }
337 |
338 | data := `{"String":null,"Ptr":null}`
339 |
340 | if err := easyjson.Unmarshal([]byte(data), &p); err != nil {
341 | t.Errorf("easyjson.Unmarshal() error: %v", err)
342 | }
343 |
344 | if p.String != str {
345 | t.Errorf("Wanted %q, got %q", str, p.String)
346 | }
347 |
348 | if p.Ptr != nil {
349 | t.Errorf("Wanted nil, got %q", *p.Ptr)
350 | }
351 | }
352 |
--------------------------------------------------------------------------------
/jwriter/writer.go:
--------------------------------------------------------------------------------
1 | // Package jwriter contains a JSON writer.
2 | package jwriter
3 |
4 | import (
5 | "fmt"
6 | "io"
7 | "math"
8 | "strconv"
9 | "unicode/utf8"
10 |
11 | "github.com/mailru/easyjson/buffer"
12 | )
13 |
14 | // Flags describe various encoding options. The behavior may be actually implemented in the encoder, but
15 | // Flags field in Writer is used to set and pass them around.
16 | type Flags int
17 |
18 | const (
19 | NilMapAsEmpty Flags = 1 << iota // Encode nil map as '{}' rather than 'null'.
20 | NilSliceAsEmpty // Encode nil slice as '[]' rather than 'null'.
21 | )
22 |
23 | // Writer is a JSON writer.
24 | type Writer struct {
25 | Flags Flags
26 |
27 | Error error
28 | Buffer buffer.Buffer
29 | NoEscapeHTML bool
30 | }
31 |
32 | // Size returns the size of the data that was written out.
33 | func (w *Writer) Size() int {
34 | return w.Buffer.Size()
35 | }
36 |
37 | // DumpTo outputs the data to given io.Writer, resetting the buffer.
38 | func (w *Writer) DumpTo(out io.Writer) (written int, err error) {
39 | return w.Buffer.DumpTo(out)
40 | }
41 |
42 | // BuildBytes returns writer data as a single byte slice. You can optionally provide one byte slice
43 | // as argument that it will try to reuse.
44 | func (w *Writer) BuildBytes(reuse ...[]byte) ([]byte, error) {
45 | if w.Error != nil {
46 | return nil, w.Error
47 | }
48 |
49 | return w.Buffer.BuildBytes(reuse...), nil
50 | }
51 |
52 | // ReadCloser returns an io.ReadCloser that can be used to read the data.
53 | // ReadCloser also resets the buffer.
54 | func (w *Writer) ReadCloser() (io.ReadCloser, error) {
55 | if w.Error != nil {
56 | return nil, w.Error
57 | }
58 |
59 | return w.Buffer.ReadCloser(), nil
60 | }
61 |
62 | // RawByte appends raw binary data to the buffer.
63 | func (w *Writer) RawByte(c byte) {
64 | w.Buffer.AppendByte(c)
65 | }
66 |
67 | // RawByte appends raw binary data to the buffer.
68 | func (w *Writer) RawString(s string) {
69 | w.Buffer.AppendString(s)
70 | }
71 |
72 | // Raw appends raw binary data to the buffer or sets the error if it is given. Useful for
73 | // calling with results of MarshalJSON-like functions.
74 | func (w *Writer) Raw(data []byte, err error) {
75 | switch {
76 | case w.Error != nil:
77 | return
78 | case err != nil:
79 | w.Error = err
80 | case len(data) > 0:
81 | w.Buffer.AppendBytes(data)
82 | default:
83 | w.RawString("null")
84 | }
85 | }
86 |
87 | // RawText encloses raw binary data in quotes and appends in to the buffer.
88 | // Useful for calling with results of MarshalText-like functions.
89 | func (w *Writer) RawText(data []byte, err error) {
90 | switch {
91 | case w.Error != nil:
92 | return
93 | case err != nil:
94 | w.Error = err
95 | default:
96 | w.String(string(data))
97 | }
98 | }
99 |
100 | // Base64Bytes appends data to the buffer after base64 encoding it
101 | func (w *Writer) Base64Bytes(data []byte) {
102 | if data == nil {
103 | w.Buffer.AppendString("null")
104 | return
105 | }
106 | w.Buffer.AppendByte('"')
107 | w.base64(data)
108 | w.Buffer.AppendByte('"')
109 | }
110 |
111 | func (w *Writer) Uint8(n uint8) {
112 | w.Buffer.EnsureSpace(3)
113 | w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10)
114 | }
115 |
116 | func (w *Writer) Uint16(n uint16) {
117 | w.Buffer.EnsureSpace(5)
118 | w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10)
119 | }
120 |
121 | func (w *Writer) Uint32(n uint32) {
122 | w.Buffer.EnsureSpace(10)
123 | w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10)
124 | }
125 |
126 | func (w *Writer) Uint(n uint) {
127 | w.Buffer.EnsureSpace(20)
128 | w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10)
129 | }
130 |
131 | func (w *Writer) Uint64(n uint64) {
132 | w.Buffer.EnsureSpace(20)
133 | w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, n, 10)
134 | }
135 |
136 | func (w *Writer) Int8(n int8) {
137 | w.Buffer.EnsureSpace(4)
138 | w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10)
139 | }
140 |
141 | func (w *Writer) Int16(n int16) {
142 | w.Buffer.EnsureSpace(6)
143 | w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10)
144 | }
145 |
146 | func (w *Writer) Int32(n int32) {
147 | w.Buffer.EnsureSpace(11)
148 | w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10)
149 | }
150 |
151 | func (w *Writer) Int(n int) {
152 | w.Buffer.EnsureSpace(21)
153 | w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10)
154 | }
155 |
156 | func (w *Writer) Int64(n int64) {
157 | w.Buffer.EnsureSpace(21)
158 | w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, n, 10)
159 | }
160 |
161 | func (w *Writer) Uint8Str(n uint8) {
162 | w.Buffer.EnsureSpace(3)
163 | w.Buffer.Buf = append(w.Buffer.Buf, '"')
164 | w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10)
165 | w.Buffer.Buf = append(w.Buffer.Buf, '"')
166 | }
167 |
168 | func (w *Writer) Uint16Str(n uint16) {
169 | w.Buffer.EnsureSpace(5)
170 | w.Buffer.Buf = append(w.Buffer.Buf, '"')
171 | w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10)
172 | w.Buffer.Buf = append(w.Buffer.Buf, '"')
173 | }
174 |
175 | func (w *Writer) Uint32Str(n uint32) {
176 | w.Buffer.EnsureSpace(10)
177 | w.Buffer.Buf = append(w.Buffer.Buf, '"')
178 | w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10)
179 | w.Buffer.Buf = append(w.Buffer.Buf, '"')
180 | }
181 |
182 | func (w *Writer) UintStr(n uint) {
183 | w.Buffer.EnsureSpace(20)
184 | w.Buffer.Buf = append(w.Buffer.Buf, '"')
185 | w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10)
186 | w.Buffer.Buf = append(w.Buffer.Buf, '"')
187 | }
188 |
189 | func (w *Writer) Uint64Str(n uint64) {
190 | w.Buffer.EnsureSpace(20)
191 | w.Buffer.Buf = append(w.Buffer.Buf, '"')
192 | w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, n, 10)
193 | w.Buffer.Buf = append(w.Buffer.Buf, '"')
194 | }
195 |
196 | func (w *Writer) UintptrStr(n uintptr) {
197 | w.Buffer.EnsureSpace(20)
198 | w.Buffer.Buf = append(w.Buffer.Buf, '"')
199 | w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10)
200 | w.Buffer.Buf = append(w.Buffer.Buf, '"')
201 | }
202 |
203 | func (w *Writer) Int8Str(n int8) {
204 | w.Buffer.EnsureSpace(4)
205 | w.Buffer.Buf = append(w.Buffer.Buf, '"')
206 | w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10)
207 | w.Buffer.Buf = append(w.Buffer.Buf, '"')
208 | }
209 |
210 | func (w *Writer) Int16Str(n int16) {
211 | w.Buffer.EnsureSpace(6)
212 | w.Buffer.Buf = append(w.Buffer.Buf, '"')
213 | w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10)
214 | w.Buffer.Buf = append(w.Buffer.Buf, '"')
215 | }
216 |
217 | func (w *Writer) Int32Str(n int32) {
218 | w.Buffer.EnsureSpace(11)
219 | w.Buffer.Buf = append(w.Buffer.Buf, '"')
220 | w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10)
221 | w.Buffer.Buf = append(w.Buffer.Buf, '"')
222 | }
223 |
224 | func (w *Writer) IntStr(n int) {
225 | w.Buffer.EnsureSpace(21)
226 | w.Buffer.Buf = append(w.Buffer.Buf, '"')
227 | w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10)
228 | w.Buffer.Buf = append(w.Buffer.Buf, '"')
229 | }
230 |
231 | func (w *Writer) Int64Str(n int64) {
232 | w.Buffer.EnsureSpace(21)
233 | w.Buffer.Buf = append(w.Buffer.Buf, '"')
234 | w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, n, 10)
235 | w.Buffer.Buf = append(w.Buffer.Buf, '"')
236 | }
237 |
238 | func (w *Writer) Float32(n float32) {
239 | if w.checkIsUnsupportedFloat(float64(n)) {
240 | return
241 | }
242 |
243 | w.Buffer.EnsureSpace(20)
244 | w.Buffer.Buf = strconv.AppendFloat(w.Buffer.Buf, float64(n), 'g', -1, 32)
245 | }
246 |
247 | func (w *Writer) Float32Str(n float32) {
248 | if w.checkIsUnsupportedFloat(float64(n)) {
249 | return
250 | }
251 |
252 | w.Buffer.EnsureSpace(20)
253 | w.Buffer.Buf = append(w.Buffer.Buf, '"')
254 | w.Buffer.Buf = strconv.AppendFloat(w.Buffer.Buf, float64(n), 'g', -1, 32)
255 | w.Buffer.Buf = append(w.Buffer.Buf, '"')
256 | }
257 |
258 | func (w *Writer) Float64(n float64) {
259 | if w.checkIsUnsupportedFloat(n) {
260 | return
261 | }
262 |
263 | w.Buffer.EnsureSpace(20)
264 | w.Buffer.Buf = strconv.AppendFloat(w.Buffer.Buf, n, 'g', -1, 64)
265 | }
266 |
267 | func (w *Writer) Float64Str(n float64) {
268 | if w.checkIsUnsupportedFloat(n) {
269 | return
270 | }
271 |
272 | w.Buffer.EnsureSpace(20)
273 | w.Buffer.Buf = append(w.Buffer.Buf, '"')
274 | w.Buffer.Buf = strconv.AppendFloat(w.Buffer.Buf, float64(n), 'g', -1, 64)
275 | w.Buffer.Buf = append(w.Buffer.Buf, '"')
276 | }
277 |
278 | func (w *Writer) Bool(v bool) {
279 | w.Buffer.EnsureSpace(5)
280 | if v {
281 | w.Buffer.Buf = append(w.Buffer.Buf, "true"...)
282 | } else {
283 | w.Buffer.Buf = append(w.Buffer.Buf, "false"...)
284 | }
285 | }
286 |
287 | const chars = "0123456789abcdef"
288 |
289 | func getTable(falseValues ...int) [128]bool {
290 | table := [128]bool{}
291 |
292 | for i := 0; i < 128; i++ {
293 | table[i] = true
294 | }
295 |
296 | for _, v := range falseValues {
297 | table[v] = false
298 | }
299 |
300 | return table
301 | }
302 |
303 | var (
304 | htmlEscapeTable = getTable(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, '"', '&', '<', '>', '\\')
305 | htmlNoEscapeTable = getTable(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, '"', '\\')
306 | )
307 |
308 | func (w *Writer) String(s string) {
309 | w.Buffer.AppendByte('"')
310 |
311 | // Portions of the string that contain no escapes are appended as
312 | // byte slices.
313 |
314 | p := 0 // last non-escape symbol
315 |
316 | escapeTable := &htmlEscapeTable
317 | if w.NoEscapeHTML {
318 | escapeTable = &htmlNoEscapeTable
319 | }
320 |
321 | for i := 0; i < len(s); {
322 | c := s[i]
323 |
324 | if c < utf8.RuneSelf {
325 | if escapeTable[c] {
326 | // single-width character, no escaping is required
327 | i++
328 | continue
329 | }
330 |
331 | w.Buffer.AppendString(s[p:i])
332 | switch c {
333 | case '\t':
334 | w.Buffer.AppendString(`\t`)
335 | case '\r':
336 | w.Buffer.AppendString(`\r`)
337 | case '\n':
338 | w.Buffer.AppendString(`\n`)
339 | case '\\':
340 | w.Buffer.AppendString(`\\`)
341 | case '"':
342 | w.Buffer.AppendString(`\"`)
343 | default:
344 | w.Buffer.AppendString(`\u00`)
345 | w.Buffer.AppendByte(chars[c>>4])
346 | w.Buffer.AppendByte(chars[c&0xf])
347 | }
348 |
349 | i++
350 | p = i
351 | continue
352 | }
353 |
354 | // broken utf
355 | runeValue, runeWidth := utf8.DecodeRuneInString(s[i:])
356 | if runeValue == utf8.RuneError && runeWidth == 1 {
357 | w.Buffer.AppendString(s[p:i])
358 | w.Buffer.AppendString(`\ufffd`)
359 | i++
360 | p = i
361 | continue
362 | }
363 |
364 | // jsonp stuff - tab separator and line separator
365 | if runeValue == '\u2028' || runeValue == '\u2029' {
366 | w.Buffer.AppendString(s[p:i])
367 | w.Buffer.AppendString(`\u202`)
368 | w.Buffer.AppendByte(chars[runeValue&0xf])
369 | i += runeWidth
370 | p = i
371 | continue
372 | }
373 | i += runeWidth
374 | }
375 | w.Buffer.AppendString(s[p:])
376 | w.Buffer.AppendByte('"')
377 | }
378 |
379 | const encode = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
380 | const padChar = '='
381 |
382 | func (w *Writer) base64(in []byte) {
383 |
384 | if len(in) == 0 {
385 | return
386 | }
387 |
388 | w.Buffer.EnsureSpace(((len(in)-1)/3 + 1) * 4)
389 |
390 | si := 0
391 | n := (len(in) / 3) * 3
392 |
393 | for si < n {
394 | // Convert 3x 8bit source bytes into 4 bytes
395 | val := uint(in[si+0])<<16 | uint(in[si+1])<<8 | uint(in[si+2])
396 |
397 | w.Buffer.Buf = append(w.Buffer.Buf, encode[val>>18&0x3F], encode[val>>12&0x3F], encode[val>>6&0x3F], encode[val&0x3F])
398 |
399 | si += 3
400 | }
401 |
402 | remain := len(in) - si
403 | if remain == 0 {
404 | return
405 | }
406 |
407 | // Add the remaining small block
408 | val := uint(in[si+0]) << 16
409 | if remain == 2 {
410 | val |= uint(in[si+1]) << 8
411 | }
412 |
413 | w.Buffer.Buf = append(w.Buffer.Buf, encode[val>>18&0x3F], encode[val>>12&0x3F])
414 |
415 | switch remain {
416 | case 2:
417 | w.Buffer.Buf = append(w.Buffer.Buf, encode[val>>6&0x3F], byte(padChar))
418 | case 1:
419 | w.Buffer.Buf = append(w.Buffer.Buf, byte(padChar), byte(padChar))
420 | }
421 | }
422 |
423 | func (w *Writer) checkIsUnsupportedFloat(val float64) bool {
424 | isUnsupported := math.IsNaN(val) || math.IsInf(val, 0)
425 | if isUnsupported && w.Error == nil {
426 | w.Error = fmt.Errorf("json: unsupported value: %v", val)
427 | }
428 |
429 | return isUnsupported
430 | }
431 |
--------------------------------------------------------------------------------
/jlexer/lexer_test.go:
--------------------------------------------------------------------------------
1 | package jlexer
2 |
3 | import (
4 | "bytes"
5 | "encoding/json"
6 | "reflect"
7 | "testing"
8 | )
9 |
10 | func TestString(t *testing.T) {
11 | for i, test := range []struct {
12 | toParse string
13 | want string
14 | wantError bool
15 | }{
16 | {toParse: `"simple string"`, want: "simple string"},
17 | {toParse: " \r\r\n\t " + `"test"`, want: "test"},
18 | {toParse: `"\n\t\"\/\\\f\r"`, want: "\n\t\"/\\\f\r"},
19 | {toParse: `"\u0020"`, want: " "},
20 | {toParse: `"\u0020-\t"`, want: " -\t"},
21 | {toParse: `"\ufffd\uFFFD"`, want: "\ufffd\ufffd"},
22 | {toParse: `"\ud83d\ude00"`, want: "😀"},
23 | {toParse: `"\ud83d\ude08"`, want: "😈"},
24 | {toParse: `"\ud8"`, wantError: true},
25 |
26 | {toParse: `"test"junk`, want: "test"},
27 |
28 | {toParse: `5`, wantError: true}, // not a string
29 | {toParse: `"\x"`, wantError: true}, // invalid escape
30 | {toParse: `"\ud800"`, want: "�"}, // invalid utf-8 char; return replacement char
31 | } {
32 | {
33 | l := Lexer{Data: []byte(test.toParse)}
34 |
35 | got := l.String()
36 | if got != test.want {
37 | t.Errorf("[%d, %q] String() = %v; want %v", i, test.toParse, got, test.want)
38 | }
39 | err := l.Error()
40 | if err != nil && !test.wantError {
41 | t.Errorf("[%d, %q] String() error: %v", i, test.toParse, err)
42 | } else if err == nil && test.wantError {
43 | t.Errorf("[%d, %q] String() ok; want error", i, test.toParse)
44 | }
45 | }
46 | {
47 | l := Lexer{Data: []byte(test.toParse)}
48 |
49 | got := l.StringIntern()
50 | if got != test.want {
51 | t.Errorf("[%d, %q] String() = %v; want %v", i, test.toParse, got, test.want)
52 | }
53 | err := l.Error()
54 | if err != nil && !test.wantError {
55 | t.Errorf("[%d, %q] String() error: %v", i, test.toParse, err)
56 | } else if err == nil && test.wantError {
57 | t.Errorf("[%d, %q] String() ok; want error", i, test.toParse)
58 | }
59 | }
60 | }
61 | }
62 |
63 | func TestStringIntern(t *testing.T) {
64 | data := []byte(`"string interning test"`)
65 | var l Lexer
66 |
67 | allocsPerRun := testing.AllocsPerRun(1000, func() {
68 | l = Lexer{Data: data}
69 | _ = l.StringIntern()
70 | })
71 | if allocsPerRun != 0 {
72 | t.Fatalf("expected 0 allocs, got %f", allocsPerRun)
73 | }
74 |
75 | allocsPerRun = testing.AllocsPerRun(1000, func() {
76 | l = Lexer{Data: data}
77 | _ = l.String()
78 | })
79 | if allocsPerRun != 1 {
80 | t.Fatalf("expected 1 allocs, got %f", allocsPerRun)
81 | }
82 | }
83 |
84 | func TestBytes(t *testing.T) {
85 | for i, test := range []struct {
86 | toParse string
87 | want string
88 | wantError bool
89 | }{
90 | {toParse: `"c2ltcGxlIHN0cmluZw=="`, want: "simple string"},
91 | {toParse: " \r\r\n\t " + `"dGVzdA=="`, want: "test"},
92 | {toParse: `"c3ViamVjdHM\/X2Q9MQ=="`, want: "subjects?_d=1"}, // base64 with forward slash escaped
93 |
94 | {toParse: `5`, wantError: true}, // not a JSON string
95 | {toParse: `"foobar"`, wantError: true}, // not base64 encoded
96 | {toParse: `"c2ltcGxlIHN0cmluZw="`, wantError: true}, // invalid base64 padding
97 | } {
98 | l := Lexer{Data: []byte(test.toParse)}
99 |
100 | got := l.Bytes()
101 | if bytes.Compare(got, []byte(test.want)) != 0 {
102 | t.Errorf("[%d, %q] Bytes() = %v; want: %v", i, test.toParse, got, []byte(test.want))
103 | }
104 | err := l.Error()
105 | if err != nil && !test.wantError {
106 | t.Errorf("[%d, %q] Bytes() error: %v", i, test.toParse, err)
107 | } else if err == nil && test.wantError {
108 | t.Errorf("[%d, %q] Bytes() ok; want error", i, test.toParse)
109 | }
110 | }
111 | }
112 |
113 | func TestNumber(t *testing.T) {
114 | for i, test := range []struct {
115 | toParse string
116 | want string
117 | wantError bool
118 | }{
119 | {toParse: "123", want: "123"},
120 | {toParse: "-123", want: "-123"},
121 | {toParse: "\r\n12.35", want: "12.35"},
122 | {toParse: "12.35e+1", want: "12.35e+1"},
123 | {toParse: "12.35e-15", want: "12.35e-15"},
124 | {toParse: "12.35E-15", want: "12.35E-15"},
125 | {toParse: "12.35E15", want: "12.35E15"},
126 |
127 | {toParse: `"a"`, wantError: true},
128 | {toParse: "123junk", wantError: true},
129 | {toParse: "1.2.3", wantError: true},
130 | {toParse: "1e2e3", wantError: true},
131 | {toParse: "1e2.3", wantError: true},
132 | } {
133 | l := Lexer{Data: []byte(test.toParse)}
134 |
135 | got := l.number()
136 | if got != test.want {
137 | t.Errorf("[%d, %q] number() = %v; want %v", i, test.toParse, got, test.want)
138 | }
139 | err := l.Error()
140 | if err != nil && !test.wantError {
141 | t.Errorf("[%d, %q] number() error: %v", i, test.toParse, err)
142 | } else if err == nil && test.wantError {
143 | t.Errorf("[%d, %q] number() ok; want error", i, test.toParse)
144 | }
145 | }
146 | }
147 |
148 | func TestBool(t *testing.T) {
149 | for i, test := range []struct {
150 | toParse string
151 | want bool
152 | wantError bool
153 | }{
154 | {toParse: "true", want: true},
155 | {toParse: "false", want: false},
156 |
157 | {toParse: "1", wantError: true},
158 | {toParse: "truejunk", wantError: true},
159 | {toParse: `false"junk"`, wantError: true},
160 | {toParse: "True", wantError: true},
161 | {toParse: "False", wantError: true},
162 | } {
163 | l := Lexer{Data: []byte(test.toParse)}
164 |
165 | got := l.Bool()
166 | if got != test.want {
167 | t.Errorf("[%d, %q] Bool() = %v; want %v", i, test.toParse, got, test.want)
168 | }
169 | err := l.Error()
170 | if err != nil && !test.wantError {
171 | t.Errorf("[%d, %q] Bool() error: %v", i, test.toParse, err)
172 | } else if err == nil && test.wantError {
173 | t.Errorf("[%d, %q] Bool() ok; want error", i, test.toParse)
174 | }
175 | }
176 | }
177 |
178 | func TestSkipRecursive(t *testing.T) {
179 | for i, test := range []struct {
180 | toParse string
181 | left string
182 | wantError bool
183 | }{
184 | {toParse: "5, 4", left: ", 4"},
185 | {toParse: "[5, 6], 4", left: ", 4"},
186 | {toParse: "[5, [7,8]]: 4", left: ": 4"},
187 |
188 | {toParse: `{"a":1}, 4`, left: ", 4"},
189 | {toParse: `{"a":1, "b":{"c": 5}, "e":[12,15]}, 4`, left: ", 4"},
190 |
191 | // array start/end chars in a string
192 | {toParse: `[5, "]"], 4`, left: ", 4"},
193 | {toParse: `[5, "\"]"], 4`, left: ", 4"},
194 | {toParse: `[5, "["], 4`, left: ", 4"},
195 | {toParse: `[5, "\"["], 4`, left: ", 4"},
196 |
197 | // object start/end chars in a string
198 | {toParse: `{"a}":1}, 4`, left: ", 4"},
199 | {toParse: `{"a\"}":1}, 4`, left: ", 4"},
200 | {toParse: `{"a{":1}, 4`, left: ", 4"},
201 | {toParse: `{"a\"{":1}, 4`, left: ", 4"},
202 |
203 | // object with double slashes at the end of string
204 | {toParse: `{"a":"hey\\"}, 4`, left: ", 4"},
205 |
206 | // make sure skipping an invalid json results in an error
207 | {toParse: `{"a": [ ##invalid json## ]}, 4`, wantError: true},
208 | {toParse: `{"a": [ [1], [ ##invalid json## ]]}, 4`, wantError: true},
209 | } {
210 | l := Lexer{Data: []byte(test.toParse)}
211 |
212 | l.SkipRecursive()
213 |
214 | got := string(l.Data[l.pos:])
215 | if got != test.left {
216 | t.Errorf("[%d, %q] SkipRecursive() left = %v; want %v", i, test.toParse, got, test.left)
217 | }
218 | err := l.Error()
219 | if err != nil && !test.wantError {
220 | t.Errorf("[%d, %q] SkipRecursive() error: %v", i, test.toParse, err)
221 | } else if err == nil && test.wantError {
222 | t.Errorf("[%d, %q] SkipRecursive() ok; want error", i, test.toParse)
223 | }
224 | }
225 | }
226 |
227 | func TestInterface(t *testing.T) {
228 | for i, test := range []struct {
229 | toParse string
230 | want interface{}
231 | wantError bool
232 | }{
233 | {toParse: "null", want: nil},
234 | {toParse: "true", want: true},
235 | {toParse: `"a"`, want: "a"},
236 | {toParse: "5", want: float64(5)},
237 |
238 | {toParse: `{}`, want: map[string]interface{}{}},
239 | {toParse: `[]`, want: []interface{}{}},
240 |
241 | {toParse: `{"a": "b"}`, want: map[string]interface{}{"a": "b"}},
242 | {toParse: `[5]`, want: []interface{}{float64(5)}},
243 |
244 | {toParse: `{"a":5 , "b" : "string"}`, want: map[string]interface{}{"a": float64(5), "b": "string"}},
245 | {toParse: `["a", 5 , null, true]`, want: []interface{}{"a", float64(5), nil, true}},
246 |
247 | {toParse: `{"a" "b"}`, wantError: true},
248 | {toParse: `{"a": "b",}`, wantError: true},
249 | {toParse: `{"a":"b","c" "b"}`, wantError: true},
250 | {toParse: `{"a": "b","c":"d",}`, wantError: true},
251 | {toParse: `{,}`, wantError: true},
252 |
253 | {toParse: `[1, 2,]`, wantError: true},
254 | {toParse: `[1 2]`, wantError: true},
255 | {toParse: `[,]`, wantError: true},
256 | } {
257 | l := Lexer{Data: []byte(test.toParse)}
258 |
259 | got := l.Interface()
260 | if !reflect.DeepEqual(got, test.want) {
261 | t.Errorf("[%d, %q] Interface() = %v; want %v", i, test.toParse, got, test.want)
262 | }
263 | err := l.Error()
264 | if err != nil && !test.wantError {
265 | t.Errorf("[%d, %q] Interface() error: %v", i, test.toParse, err)
266 | } else if err == nil && test.wantError {
267 | t.Errorf("[%d, %q] Interface() ok; want error", i, test.toParse)
268 | }
269 | }
270 | }
271 |
272 | func TestConsumed(t *testing.T) {
273 | for i, test := range []struct {
274 | toParse string
275 | wantError bool
276 | }{
277 | {toParse: "", wantError: false},
278 | {toParse: " ", wantError: false},
279 | {toParse: "\r\n", wantError: false},
280 | {toParse: "\t\t", wantError: false},
281 |
282 | {toParse: "{", wantError: true},
283 | } {
284 | l := Lexer{Data: []byte(test.toParse)}
285 | l.Consumed()
286 |
287 | err := l.Error()
288 | if err != nil && !test.wantError {
289 | t.Errorf("[%d, %q] Consumed() error: %v", i, test.toParse, err)
290 | } else if err == nil && test.wantError {
291 | t.Errorf("[%d, %q] Consumed() ok; want error", i, test.toParse)
292 | }
293 | }
294 | }
295 |
296 | func TestJsonNumber(t *testing.T) {
297 | for i, test := range []struct {
298 | toParse string
299 | want json.Number
300 | wantLexerError bool
301 | wantValue interface{}
302 | wantValueError bool
303 | }{
304 | {toParse: `10`, want: json.Number("10"), wantValue: int64(10)},
305 | {toParse: `0`, want: json.Number("0"), wantValue: int64(0)},
306 | {toParse: `0.12`, want: json.Number("0.12"), wantValue: 0.12},
307 | {toParse: `25E-4`, want: json.Number("25E-4"), wantValue: 25e-4},
308 |
309 | {toParse: `"10"`, want: json.Number("10"), wantValue: int64(10)},
310 | {toParse: `"0"`, want: json.Number("0"), wantValue: int64(0)},
311 | {toParse: `"0.12"`, want: json.Number("0.12"), wantValue: 0.12},
312 | {toParse: `"25E-4"`, want: json.Number("25E-4"), wantValue: 25e-4},
313 |
314 | {toParse: `"foo"`, want: json.Number("foo"), wantValueError: true},
315 | {toParse: `null`, want: json.Number(""), wantValueError: true},
316 |
317 | {toParse: `"a""`, want: json.Number("a"), wantValueError: true},
318 |
319 | {toParse: `[1]`, want: json.Number(""), wantLexerError: true, wantValueError: true},
320 | {toParse: `{}`, want: json.Number(""), wantLexerError: true, wantValueError: true},
321 | {toParse: `a`, want: json.Number(""), wantLexerError: true, wantValueError: true},
322 | } {
323 | l := Lexer{Data: []byte(test.toParse)}
324 |
325 | got := l.JsonNumber()
326 | if got != test.want {
327 | t.Errorf("[%d, %q] JsonNumber() = %v; want %v", i, test.toParse, got, test.want)
328 | }
329 |
330 | err := l.Error()
331 | if err != nil && !test.wantLexerError {
332 | t.Errorf("[%d, %q] JsonNumber() lexer error: %v", i, test.toParse, err)
333 | } else if err == nil && test.wantLexerError {
334 | t.Errorf("[%d, %q] JsonNumber() ok; want lexer error", i, test.toParse)
335 | }
336 |
337 | var valueErr error
338 | var gotValue interface{}
339 | switch test.wantValue.(type) {
340 | case float64:
341 | gotValue, valueErr = got.Float64()
342 | default:
343 | gotValue, valueErr = got.Int64()
344 | }
345 |
346 | if !reflect.DeepEqual(gotValue, test.wantValue) && !test.wantLexerError && !test.wantValueError {
347 | t.Errorf("[%d, %q] JsonNumber() = %v; want %v", i, test.toParse, gotValue, test.wantValue)
348 | }
349 |
350 | if valueErr != nil && !test.wantValueError {
351 | t.Errorf("[%d, %q] JsonNumber() value error: %v", i, test.toParse, valueErr)
352 | } else if valueErr == nil && test.wantValueError {
353 | t.Errorf("[%d, %q] JsonNumber() ok; want value error", i, test.toParse)
354 | }
355 | }
356 | }
357 |
358 | func TestFetchStringUnterminatedString(t *testing.T) {
359 | for _, test := range []struct {
360 | data []byte
361 | }{
362 | {data: []byte(`"sting without trailing quote`)},
363 | {data: []byte(`"\"`)},
364 | {data: []byte{'"'}},
365 | } {
366 | l := Lexer{Data: test.data}
367 | l.fetchString()
368 | if l.pos > len(l.Data) {
369 | t.Errorf("fetchString(%s): pos=%v should not be greater than length of Data = %v", test.data, l.pos, len(l.Data))
370 | }
371 | if l.Error() == nil {
372 | t.Errorf("fetchString(%s): should add parsing error", test.data)
373 | }
374 | }
375 | }
376 |
377 | func TestCurrentToken(t *testing.T) {
378 | data := []byte(`{"foo"`)
379 | tokens := []TokenKind{TokenDelim, TokenString, TokenUndef}
380 | l := Lexer{Data: data}
381 |
382 | for _, want := range tokens {
383 | got := l.CurrentToken()
384 | if got != want {
385 | t.Errorf("CurrentToken() = %v; want %v (err %s)", got, want, l.Error())
386 | }
387 |
388 | l.Skip()
389 | }
390 | }
391 |
--------------------------------------------------------------------------------
/benchmark/data_var.go:
--------------------------------------------------------------------------------
1 | package benchmark
2 |
3 | var largeStructData = LargeStruct{
4 | SearchMetadata: SearchMetadata{
5 | CompletedIn: 0.035,
6 | Count: 4,
7 | MaxID: 250126199840518145,
8 | MaxIDStr: "250126199840518145",
9 | NextResults: "?max_id=249279667666817023&q=%23freebandnames&count=4&include_entities=1&result_type=mixed",
10 | Query: "%23freebandnames",
11 | RefreshURL: "?since_id=250126199840518145&q=%23freebandnames&result_type=mixed&include_entities=1",
12 | SinceID: 24012619984051000,
13 | SinceIDStr: "24012619984051000",
14 | },
15 | Statuses: []Status{
16 | {
17 | Contributors: nil,
18 | Coordinates: nil,
19 | CreatedAt: "Mon Sep 24 03:35:21 +0000 2012",
20 | Entities: Entities{
21 | Hashtags: []Hashtag{{
22 | Indices: []int{20, 34},
23 | Text: "freebandnames"},
24 | },
25 | Urls: []*string{},
26 | UserMentions: []*string{},
27 | },
28 | Favorited: false,
29 | Geo: nil,
30 | ID: 250075927172759552,
31 | IDStr: "250075927172759552",
32 | InReplyToScreenName: nil,
33 | InReplyToStatusID: nil,
34 | InReplyToStatusIDStr: nil,
35 | InReplyToUserID: nil,
36 | InReplyToUserIDStr: nil,
37 | Metadata: StatusMetadata{
38 | IsoLanguageCode: "en",
39 | ResultType: "recent",
40 | },
41 | Place: nil,
42 | RetweetCount: 0,
43 | Retweeted: false,
44 | Source: "Twitter for Mac",
45 | Text: "Aggressive Ponytail #freebandnames",
46 | Truncated: false,
47 | User: User{
48 | ContributorsEnabled: false,
49 | CreatedAt: "Mon Apr 26 06:01:55 +0000 2010",
50 | DefaultProfile: true,
51 | DefaultProfileImage: false,
52 | Description: "Born 330 Live 310",
53 | Entities: UserEntities{
54 | Description: UserEntityDescription{
55 | Urls: []*string{},
56 | },
57 | URL: UserEntityURL{
58 | Urls: []URL{{
59 | ExpandedURL: nil,
60 | Indices: []int{0, 0},
61 | URL: "",
62 | }},
63 | },
64 | },
65 | FavouritesCount: 0,
66 | FollowRequestSent: nil,
67 | FollowersCount: 70,
68 | Following: nil,
69 | FriendsCount: 110,
70 | GeoEnabled: true,
71 | ID: 137238150,
72 | IDStr: "137238150",
73 | IsTranslator: false,
74 | Lang: "en",
75 | ListedCount: 2,
76 | Location: "LA, CA",
77 | Name: "Sean Cummings",
78 | Notifications: nil,
79 | ProfileBackgroundColor: "C0DEED",
80 | ProfileBackgroundImageURL: "http://a0.twimg.com/images/themes/theme1/bg.png",
81 | ProfileBackgroundImageURLHTTPS: "https://si0.twimg.com/images/themes/theme1/bg.png",
82 | ProfileBackgroundTile: false,
83 | ProfileImageURL: "http://a0.twimg.com/profile_images/2359746665/1v6zfgqo8g0d3mk7ii5s_normal.jpeg",
84 | ProfileImageURLHTTPS: "https://si0.twimg.com/profile_images/2359746665/1v6zfgqo8g0d3mk7ii5s_normal.jpeg",
85 | ProfileLinkColor: "0084B4",
86 | ProfileSidebarBorderColor: "C0DEED",
87 | ProfileSidebarFillColor: "DDEEF6",
88 | ProfileTextColor: "333333",
89 | ProfileUseBackgroundImage: true,
90 | Protected: false,
91 | ScreenName: "sean_cummings",
92 | ShowAllInlineMedia: false,
93 | StatusesCount: 579,
94 | TimeZone: "Pacific Time (US & Canada)",
95 | URL: nil,
96 | UtcOffset: -28800,
97 | Verified: false,
98 | },
99 | },
100 | {
101 | Contributors: nil,
102 | Coordinates: nil,
103 | CreatedAt: "Fri Sep 21 23:40:54 +0000 2012",
104 | Entities: Entities{
105 | Hashtags: []Hashtag{{
106 | Indices: []int{20, 34},
107 | Text: "FreeBandNames",
108 | }},
109 | Urls: []*string{},
110 | UserMentions: []*string{},
111 | },
112 | Favorited: false,
113 | Geo: nil,
114 | ID: 249292149810667520,
115 | IDStr: "249292149810667520",
116 | InReplyToScreenName: nil,
117 | InReplyToStatusID: nil,
118 | InReplyToStatusIDStr: nil,
119 | InReplyToUserID: nil,
120 | InReplyToUserIDStr: nil,
121 | Metadata: StatusMetadata{
122 | IsoLanguageCode: "pl",
123 | ResultType: "recent",
124 | },
125 | Place: nil,
126 | RetweetCount: 0,
127 | Retweeted: false,
128 | Source: "web",
129 | Text: "Thee Namaste Nerdz. #FreeBandNames",
130 | Truncated: false,
131 | User: User{
132 | ContributorsEnabled: false,
133 | CreatedAt: "Tue Apr 07 19:05:07 +0000 2009",
134 | DefaultProfile: false,
135 | DefaultProfileImage: false,
136 | Description: "You will come to Durham, North Carolina. I will sell you some records then, here in Durham, North Carolina. Fun will happen.",
137 | Entities: UserEntities{
138 | Description: UserEntityDescription{Urls: []*string{}},
139 | URL: UserEntityURL{
140 | Urls: []URL{{
141 | ExpandedURL: nil,
142 | Indices: []int{0, 32},
143 | URL: "http://bullcityrecords.com/wnng/"}},
144 | },
145 | },
146 | FavouritesCount: 8,
147 | FollowRequestSent: nil,
148 | FollowersCount: 2052,
149 | Following: nil,
150 | FriendsCount: 348,
151 | GeoEnabled: false,
152 | ID: 29516238,
153 | IDStr: "29516238",
154 | IsTranslator: false,
155 | Lang: "en",
156 | ListedCount: 118,
157 | Location: "Durham, NC",
158 | Name: "Chaz Martenstein",
159 | Notifications: nil,
160 | ProfileBackgroundColor: "9AE4E8",
161 | ProfileBackgroundImageURL: "http://a0.twimg.com/profile_background_images/9423277/background_tile.bmp",
162 | ProfileBackgroundImageURLHTTPS: "https://si0.twimg.com/profile_background_images/9423277/background_tile.bmp",
163 | ProfileBackgroundTile: true,
164 | ProfileImageURL: "http://a0.twimg.com/profile_images/447958234/Lichtenstein_normal.jpg",
165 | ProfileImageURLHTTPS: "https://si0.twimg.com/profile_images/447958234/Lichtenstein_normal.jpg",
166 | ProfileLinkColor: "0084B4",
167 | ProfileSidebarBorderColor: "BDDCAD",
168 | ProfileSidebarFillColor: "DDFFCC",
169 | ProfileTextColor: "333333",
170 | ProfileUseBackgroundImage: true,
171 | Protected: false,
172 | ScreenName: "bullcityrecords",
173 | ShowAllInlineMedia: true,
174 | StatusesCount: 7579,
175 | TimeZone: "Eastern Time (US & Canada)",
176 | URL: nil,
177 | UtcOffset: -18000,
178 | Verified: false,
179 | },
180 | },
181 | {
182 | Contributors: nil,
183 | Coordinates: nil,
184 | CreatedAt: "Fri Sep 21 23:30:20 +0000 2012",
185 | Entities: Entities{
186 | Hashtags: []Hashtag{{
187 | Indices: []int{29, 43},
188 | Text: "freebandnames",
189 | }},
190 | Urls: []*string{},
191 | UserMentions: []*string{},
192 | },
193 | Favorited: false,
194 | Geo: nil,
195 | ID: 249289491129438208,
196 | IDStr: "249289491129438208",
197 | InReplyToScreenName: nil,
198 | InReplyToStatusID: nil,
199 | InReplyToStatusIDStr: nil,
200 | InReplyToUserID: nil,
201 | InReplyToUserIDStr: nil,
202 | Metadata: StatusMetadata{
203 | IsoLanguageCode: "en",
204 | ResultType: "recent",
205 | },
206 | Place: nil,
207 | RetweetCount: 0,
208 | Retweeted: false,
209 | Source: "web",
210 | Text: "Mexican Heaven, Mexican Hell #freebandnames",
211 | Truncated: false,
212 | User: User{
213 | ContributorsEnabled: false,
214 | CreatedAt: "Tue Sep 01 21:21:35 +0000 2009",
215 | DefaultProfile: false,
216 | DefaultProfileImage: false,
217 | Description: "Science Fiction Writer, sort of. Likes Superheroes, Mole People, Alt. Timelines.",
218 | Entities: UserEntities{
219 | Description: UserEntityDescription{
220 | Urls: nil,
221 | },
222 | URL: UserEntityURL{
223 | Urls: []URL{{
224 | ExpandedURL: nil,
225 | Indices: []int{0, 0},
226 | URL: "",
227 | }},
228 | },
229 | },
230 | FavouritesCount: 19,
231 | FollowRequestSent: nil,
232 | FollowersCount: 63,
233 | Following: nil,
234 | FriendsCount: 63,
235 | GeoEnabled: false,
236 | ID: 70789458,
237 | IDStr: "70789458",
238 | IsTranslator: false,
239 | Lang: "en",
240 | ListedCount: 1,
241 | Location: "Kingston New York",
242 | Name: "Thomas John Wakeman",
243 | Notifications: nil,
244 | ProfileBackgroundColor: "352726",
245 | ProfileBackgroundImageURL: "http://a0.twimg.com/images/themes/theme5/bg.gif",
246 | ProfileBackgroundImageURLHTTPS: "https://si0.twimg.com/images/themes/theme5/bg.gif",
247 | ProfileBackgroundTile: false,
248 | ProfileImageURL: "http://a0.twimg.com/profile_images/2219333930/Froggystyle_normal.png",
249 | ProfileImageURLHTTPS: "https://si0.twimg.com/profile_images/2219333930/Froggystyle_normal.png",
250 | ProfileLinkColor: "D02B55",
251 | ProfileSidebarBorderColor: "829D5E",
252 | ProfileSidebarFillColor: "99CC33",
253 | ProfileTextColor: "3E4415",
254 | ProfileUseBackgroundImage: true,
255 | Protected: false,
256 | ScreenName: "MonkiesFist",
257 | ShowAllInlineMedia: false,
258 | StatusesCount: 1048,
259 | TimeZone: "Eastern Time (US & Canada)",
260 | URL: nil,
261 | UtcOffset: -18000,
262 | Verified: false,
263 | },
264 | },
265 | {
266 | Contributors: nil,
267 | Coordinates: nil,
268 | CreatedAt: "Fri Sep 21 22:51:18 +0000 2012",
269 | Entities: Entities{
270 | Hashtags: []Hashtag{{
271 | Indices: []int{20, 34},
272 | Text: "freebandnames",
273 | }},
274 | Urls: []*string{},
275 | UserMentions: []*string{},
276 | },
277 | Favorited: false,
278 | Geo: nil,
279 | ID: 249279667666817024,
280 | IDStr: "249279667666817024",
281 | InReplyToScreenName: nil,
282 | InReplyToStatusID: nil,
283 | InReplyToStatusIDStr: nil,
284 | InReplyToUserID: nil,
285 | InReplyToUserIDStr: nil,
286 | Metadata: StatusMetadata{
287 | IsoLanguageCode: "en",
288 | ResultType: "recent",
289 | },
290 | Place: nil,
291 | RetweetCount: 0,
292 | Retweeted: false,
293 | Source: "Twitter for iPhone",
294 | Text: "The Foolish Mortals #freebandnames",
295 | Truncated: false,
296 | User: User{
297 | ContributorsEnabled: false,
298 | CreatedAt: "Mon May 04 00:05:00 +0000 2009",
299 | DefaultProfile: false,
300 | DefaultProfileImage: false,
301 | Description: "Cartoonist, Illustrator, and T-Shirt connoisseur",
302 | Entities: UserEntities{
303 | Description: UserEntityDescription{
304 | Urls: []*string{},
305 | },
306 | URL: UserEntityURL{
307 | Urls: []URL{{
308 | ExpandedURL: nil,
309 | Indices: []int{0, 24},
310 | URL: "http://www.omnitarian.me",
311 | }},
312 | },
313 | },
314 | FavouritesCount: 647,
315 | FollowRequestSent: nil,
316 | FollowersCount: 608,
317 | Following: nil,
318 | FriendsCount: 249,
319 | GeoEnabled: false,
320 | ID: 37539828,
321 | IDStr: "37539828",
322 | IsTranslator: false,
323 | Lang: "en",
324 | ListedCount: 52,
325 | Location: "Wisconsin, USA",
326 | Name: "Marty Elmer",
327 | Notifications: nil,
328 | ProfileBackgroundColor: "EEE3C4",
329 | ProfileBackgroundImageURL: "http://a0.twimg.com/profile_background_images/106455659/rect6056-9.png",
330 | ProfileBackgroundImageURLHTTPS: "https://si0.twimg.com/profile_background_images/106455659/rect6056-9.png",
331 | ProfileBackgroundTile: true,
332 | ProfileImageURL: "http://a0.twimg.com/profile_images/1629790393/shrinker_2000_trans_normal.png",
333 | ProfileImageURLHTTPS: "https://si0.twimg.com/profile_images/1629790393/shrinker_2000_trans_normal.png",
334 | ProfileLinkColor: "3B2A26",
335 | ProfileSidebarBorderColor: "615A44",
336 | ProfileSidebarFillColor: "BFAC83",
337 | ProfileTextColor: "000000",
338 | ProfileUseBackgroundImage: true,
339 | Protected: false,
340 | ScreenName: "Omnitarian",
341 | ShowAllInlineMedia: true,
342 | StatusesCount: 3575,
343 | TimeZone: "Central Time (US & Canada)",
344 | URL: nil,
345 | UtcOffset: -21600,
346 | Verified: false,
347 | },
348 | },
349 | },
350 | }
351 |
--------------------------------------------------------------------------------
/benchmark/example.json:
--------------------------------------------------------------------------------
1 | {
2 | "statuses": [
3 | {
4 | "coordinates": null,
5 | "favorited": false,
6 | "truncated": false,
7 | "created_at": "Mon Sep 24 03:35:21 +0000 2012",
8 | "id_str": "250075927172759552",
9 | "entities": {
10 | "urls": [
11 |
12 | ],
13 | "hashtags": [
14 | {
15 | "text": "freebandnames",
16 | "indices": [
17 | 20,
18 | 34
19 | ]
20 | }
21 | ],
22 | "user_mentions": [
23 |
24 | ]
25 | },
26 | "in_reply_to_user_id_str": null,
27 | "contributors": null,
28 | "text": "Aggressive Ponytail #freebandnames",
29 | "metadata": {
30 | "iso_language_code": "en",
31 | "result_type": "recent"
32 | },
33 | "retweet_count": 0,
34 | "in_reply_to_status_id_str": null,
35 | "id": 250075927172759552,
36 | "geo": null,
37 | "retweeted": false,
38 | "in_reply_to_user_id": null,
39 | "place": null,
40 | "user": {
41 | "profile_sidebar_fill_color": "DDEEF6",
42 | "profile_sidebar_border_color": "C0DEED",
43 | "profile_background_tile": false,
44 | "name": "Sean Cummings",
45 | "profile_image_url": "http://a0.twimg.com/profile_images/2359746665/1v6zfgqo8g0d3mk7ii5s_normal.jpeg",
46 | "created_at": "Mon Apr 26 06:01:55 +0000 2010",
47 | "location": "LA, CA",
48 | "follow_request_sent": null,
49 | "profile_link_color": "0084B4",
50 | "is_translator": false,
51 | "id_str": "137238150",
52 | "entities": {
53 | "url": {
54 | "urls": [
55 | {
56 | "expanded_url": null,
57 | "url": "",
58 | "indices": [
59 | 0,
60 | 0
61 | ]
62 | }
63 | ]
64 | },
65 | "description": {
66 | "urls": [
67 |
68 | ]
69 | }
70 | },
71 | "default_profile": true,
72 | "contributors_enabled": false,
73 | "favourites_count": 0,
74 | "url": null,
75 | "profile_image_url_https": "https://si0.twimg.com/profile_images/2359746665/1v6zfgqo8g0d3mk7ii5s_normal.jpeg",
76 | "utc_offset": -28800,
77 | "id": 137238150,
78 | "profile_use_background_image": true,
79 | "listed_count": 2,
80 | "profile_text_color": "333333",
81 | "lang": "en",
82 | "followers_count": 70,
83 | "protected": false,
84 | "notifications": null,
85 | "profile_background_image_url_https": "https://si0.twimg.com/images/themes/theme1/bg.png",
86 | "profile_background_color": "C0DEED",
87 | "verified": false,
88 | "geo_enabled": true,
89 | "time_zone": "Pacific Time (US & Canada)",
90 | "description": "Born 330 Live 310",
91 | "default_profile_image": false,
92 | "profile_background_image_url": "http://a0.twimg.com/images/themes/theme1/bg.png",
93 | "statuses_count": 579,
94 | "friends_count": 110,
95 | "following": null,
96 | "show_all_inline_media": false,
97 | "screen_name": "sean_cummings"
98 | },
99 | "in_reply_to_screen_name": null,
100 | "source": "Twitter for Mac",
101 | "in_reply_to_status_id": null
102 | },
103 | {
104 | "coordinates": null,
105 | "favorited": false,
106 | "truncated": false,
107 | "created_at": "Fri Sep 21 23:40:54 +0000 2012",
108 | "id_str": "249292149810667520",
109 | "entities": {
110 | "urls": [
111 |
112 | ],
113 | "hashtags": [
114 | {
115 | "text": "FreeBandNames",
116 | "indices": [
117 | 20,
118 | 34
119 | ]
120 | }
121 | ],
122 | "user_mentions": [
123 |
124 | ]
125 | },
126 | "in_reply_to_user_id_str": null,
127 | "contributors": null,
128 | "text": "Thee Namaste Nerdz. #FreeBandNames",
129 | "metadata": {
130 | "iso_language_code": "pl",
131 | "result_type": "recent"
132 | },
133 | "retweet_count": 0,
134 | "in_reply_to_status_id_str": null,
135 | "id": 249292149810667520,
136 | "geo": null,
137 | "retweeted": false,
138 | "in_reply_to_user_id": null,
139 | "place": null,
140 | "user": {
141 | "profile_sidebar_fill_color": "DDFFCC",
142 | "profile_sidebar_border_color": "BDDCAD",
143 | "profile_background_tile": true,
144 | "name": "Chaz Martenstein",
145 | "profile_image_url": "http://a0.twimg.com/profile_images/447958234/Lichtenstein_normal.jpg",
146 | "created_at": "Tue Apr 07 19:05:07 +0000 2009",
147 | "location": "Durham, NC",
148 | "follow_request_sent": null,
149 | "profile_link_color": "0084B4",
150 | "is_translator": false,
151 | "id_str": "29516238",
152 | "entities": {
153 | "url": {
154 | "urls": [
155 | {
156 | "expanded_url": null,
157 | "url": "http://bullcityrecords.com/wnng/",
158 | "indices": [
159 | 0,
160 | 32
161 | ]
162 | }
163 | ]
164 | },
165 | "description": {
166 | "urls": [
167 |
168 | ]
169 | }
170 | },
171 | "default_profile": false,
172 | "contributors_enabled": false,
173 | "favourites_count": 8,
174 | "url": "http://bullcityrecords.com/wnng/",
175 | "profile_image_url_https": "https://si0.twimg.com/profile_images/447958234/Lichtenstein_normal.jpg",
176 | "utc_offset": -18000,
177 | "id": 29516238,
178 | "profile_use_background_image": true,
179 | "listed_count": 118,
180 | "profile_text_color": "333333",
181 | "lang": "en",
182 | "followers_count": 2052,
183 | "protected": false,
184 | "notifications": null,
185 | "profile_background_image_url_https": "https://si0.twimg.com/profile_background_images/9423277/background_tile.bmp",
186 | "profile_background_color": "9AE4E8",
187 | "verified": false,
188 | "geo_enabled": false,
189 | "time_zone": "Eastern Time (US & Canada)",
190 | "description": "You will come to Durham, North Carolina. I will sell you some records then, here in Durham, North Carolina. Fun will happen.",
191 | "default_profile_image": false,
192 | "profile_background_image_url": "http://a0.twimg.com/profile_background_images/9423277/background_tile.bmp",
193 | "statuses_count": 7579,
194 | "friends_count": 348,
195 | "following": null,
196 | "show_all_inline_media": true,
197 | "screen_name": "bullcityrecords"
198 | },
199 | "in_reply_to_screen_name": null,
200 | "source": "web",
201 | "in_reply_to_status_id": null
202 | },
203 | {
204 | "coordinates": null,
205 | "favorited": false,
206 | "truncated": false,
207 | "created_at": "Fri Sep 21 23:30:20 +0000 2012",
208 | "id_str": "249289491129438208",
209 | "entities": {
210 | "urls": [
211 |
212 | ],
213 | "hashtags": [
214 | {
215 | "text": "freebandnames",
216 | "indices": [
217 | 29,
218 | 43
219 | ]
220 | }
221 | ],
222 | "user_mentions": [
223 |
224 | ]
225 | },
226 | "in_reply_to_user_id_str": null,
227 | "contributors": null,
228 | "text": "Mexican Heaven, Mexican Hell #freebandnames",
229 | "metadata": {
230 | "iso_language_code": "en",
231 | "result_type": "recent"
232 | },
233 | "retweet_count": 0,
234 | "in_reply_to_status_id_str": null,
235 | "id": 249289491129438208,
236 | "geo": null,
237 | "retweeted": false,
238 | "in_reply_to_user_id": null,
239 | "place": null,
240 | "user": {
241 | "profile_sidebar_fill_color": "99CC33",
242 | "profile_sidebar_border_color": "829D5E",
243 | "profile_background_tile": false,
244 | "name": "Thomas John Wakeman",
245 | "profile_image_url": "http://a0.twimg.com/profile_images/2219333930/Froggystyle_normal.png",
246 | "created_at": "Tue Sep 01 21:21:35 +0000 2009",
247 | "location": "Kingston New York",
248 | "follow_request_sent": null,
249 | "profile_link_color": "D02B55",
250 | "is_translator": false,
251 | "id_str": "70789458",
252 | "entities": {
253 | "url": {
254 | "urls": [
255 | {
256 | "expanded_url": null,
257 | "url": "",
258 | "indices": [
259 | 0,
260 | 0
261 | ]
262 | }
263 | ]
264 | },
265 | "description": {
266 | "urls": [
267 |
268 | ]
269 | }
270 | },
271 | "default_profile": false,
272 | "contributors_enabled": false,
273 | "favourites_count": 19,
274 | "url": null,
275 | "profile_image_url_https": "https://si0.twimg.com/profile_images/2219333930/Froggystyle_normal.png",
276 | "utc_offset": -18000,
277 | "id": 70789458,
278 | "profile_use_background_image": true,
279 | "listed_count": 1,
280 | "profile_text_color": "3E4415",
281 | "lang": "en",
282 | "followers_count": 63,
283 | "protected": false,
284 | "notifications": null,
285 | "profile_background_image_url_https": "https://si0.twimg.com/images/themes/theme5/bg.gif",
286 | "profile_background_color": "352726",
287 | "verified": false,
288 | "geo_enabled": false,
289 | "time_zone": "Eastern Time (US & Canada)",
290 | "description": "Science Fiction Writer, sort of. Likes Superheroes, Mole People, Alt. Timelines.",
291 | "default_profile_image": false,
292 | "profile_background_image_url": "http://a0.twimg.com/images/themes/theme5/bg.gif",
293 | "statuses_count": 1048,
294 | "friends_count": 63,
295 | "following": null,
296 | "show_all_inline_media": false,
297 | "screen_name": "MonkiesFist"
298 | },
299 | "in_reply_to_screen_name": null,
300 | "source": "web",
301 | "in_reply_to_status_id": null
302 | },
303 | {
304 | "coordinates": null,
305 | "favorited": false,
306 | "truncated": false,
307 | "created_at": "Fri Sep 21 22:51:18 +0000 2012",
308 | "id_str": "249279667666817024",
309 | "entities": {
310 | "urls": [
311 |
312 | ],
313 | "hashtags": [
314 | {
315 | "text": "freebandnames",
316 | "indices": [
317 | 20,
318 | 34
319 | ]
320 | }
321 | ],
322 | "user_mentions": [
323 |
324 | ]
325 | },
326 | "in_reply_to_user_id_str": null,
327 | "contributors": null,
328 | "text": "The Foolish Mortals #freebandnames",
329 | "metadata": {
330 | "iso_language_code": "en",
331 | "result_type": "recent"
332 | },
333 | "retweet_count": 0,
334 | "in_reply_to_status_id_str": null,
335 | "id": 249279667666817024,
336 | "geo": null,
337 | "retweeted": false,
338 | "in_reply_to_user_id": null,
339 | "place": null,
340 | "user": {
341 | "profile_sidebar_fill_color": "BFAC83",
342 | "profile_sidebar_border_color": "615A44",
343 | "profile_background_tile": true,
344 | "name": "Marty Elmer",
345 | "profile_image_url": "http://a0.twimg.com/profile_images/1629790393/shrinker_2000_trans_normal.png",
346 | "created_at": "Mon May 04 00:05:00 +0000 2009",
347 | "location": "Wisconsin, USA",
348 | "follow_request_sent": null,
349 | "profile_link_color": "3B2A26",
350 | "is_translator": false,
351 | "id_str": "37539828",
352 | "entities": {
353 | "url": {
354 | "urls": [
355 | {
356 | "expanded_url": null,
357 | "url": "http://www.omnitarian.me",
358 | "indices": [
359 | 0,
360 | 24
361 | ]
362 | }
363 | ]
364 | },
365 | "description": {
366 | "urls": [
367 |
368 | ]
369 | }
370 | },
371 | "default_profile": false,
372 | "contributors_enabled": false,
373 | "favourites_count": 647,
374 | "url": "http://www.omnitarian.me",
375 | "profile_image_url_https": "https://si0.twimg.com/profile_images/1629790393/shrinker_2000_trans_normal.png",
376 | "utc_offset": -21600,
377 | "id": 37539828,
378 | "profile_use_background_image": true,
379 | "listed_count": 52,
380 | "profile_text_color": "000000",
381 | "lang": "en",
382 | "followers_count": 608,
383 | "protected": false,
384 | "notifications": null,
385 | "profile_background_image_url_https": "https://si0.twimg.com/profile_background_images/106455659/rect6056-9.png",
386 | "profile_background_color": "EEE3C4",
387 | "verified": false,
388 | "geo_enabled": false,
389 | "time_zone": "Central Time (US & Canada)",
390 | "description": "Cartoonist, Illustrator, and T-Shirt connoisseur",
391 | "default_profile_image": false,
392 | "profile_background_image_url": "http://a0.twimg.com/profile_background_images/106455659/rect6056-9.png",
393 | "statuses_count": 3575,
394 | "friends_count": 249,
395 | "following": null,
396 | "show_all_inline_media": true,
397 | "screen_name": "Omnitarian"
398 | },
399 | "in_reply_to_screen_name": null,
400 | "source": "Twitter for iPhone",
401 | "in_reply_to_status_id": null
402 | }
403 | ],
404 | "search_metadata": {
405 | "max_id": 250126199840518145,
406 | "since_id": 24012619984051000,
407 | "refresh_url": "?since_id=250126199840518145&q=%23freebandnames&result_type=mixed&include_entities=1",
408 | "next_results": "?max_id=249279667666817023&q=%23freebandnames&count=4&include_entities=1&result_type=mixed",
409 | "count": 4,
410 | "completed_in": 0.035,
411 | "since_id_str": "24012619984051000",
412 | "query": "%23freebandnames",
413 | "max_id_str": "250126199840518145"
414 | }
415 | }
416 |
--------------------------------------------------------------------------------