pax_global_header00006660000000000000000000000064136533166510014523gustar00rootroot0000000000000052 comment=6f16c3286d326c3ebe67ffa7f1be77eb187212cb golang-github-francoispqt-gojay-1.2.13/000077500000000000000000000000001365331665100177745ustar00rootroot00000000000000golang-github-francoispqt-gojay-1.2.13/.gitignore000066400000000000000000000000421365331665100217600ustar00rootroot00000000000000vendor *.out *.log *.test .vscode golang-github-francoispqt-gojay-1.2.13/.travis.yml000066400000000000000000000005141365331665100221050ustar00rootroot00000000000000language: go go: - "1.10.x" - "1.11.x" - "1.12.x" script: - go get github.com/golang/dep/cmd/dep github.com/stretchr/testify - dep ensure -v -vendor-only - go test ./gojay/codegen/test/... -race - go test -race -coverprofile=coverage.txt -covermode=atomic after_success: - bash <(curl -s https://codecov.io/bash) golang-github-francoispqt-gojay-1.2.13/Gopkg.lock000066400000000000000000000102431365331665100217150ustar00rootroot00000000000000# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. [[projects]] digest = "1:1a37f9f2ae10d161d9688fb6008ffa14e1631e5068cc3e9698008b9e8d40d575" name = "cloud.google.com/go" packages = ["compute/metadata"] pruneopts = "" revision = "457ea5c15ccf3b87db582c450e80101989da35f7" version = "v0.40.0" [[projects]] digest = "1:968d8903d598e3fae738325d3410f33f07ea6a2b9ee5591e9c262ee37df6845a" name = "github.com/go-errors/errors" packages = ["."] pruneopts = "" revision = "a6af135bd4e28680facf08a3d206b454abc877a4" version = "v1.0.1" [[projects]] digest = "1:529d738b7976c3848cae5cf3a8036440166835e389c1f617af701eeb12a0518d" name = "github.com/golang/protobuf" packages = ["proto"] pruneopts = "" revision = "b5d812f8a3706043e23a9cd5babf2e5423744d30" version = "v1.3.1" [[projects]] branch = "master" digest = "1:cae59d7b8243c671c9f544965522ba35c0fec48ee80adb9f1400cd2f33abbbec" name = "github.com/mailru/easyjson" packages = [ ".", "buffer", "jlexer", "jwriter", ] pruneopts = "" revision = "1ea4449da9834f4d333f1cc461c374aea217d249" [[projects]] digest = "1:1d7e1867c49a6dd9856598ef7c3123604ea3daabf5b83f303ff457bcbc410b1d" name = "github.com/pkg/errors" packages = ["."] pruneopts = "" revision = "ba968bfe8b2f7e042a574c888954fccecfa385b4" version = "v0.8.1" [[projects]] digest = "1:8d4bbd8ab012efc77ab6b97286f2aff262bcdeac9803bb57d75cf7d0a5e6a877" name = "github.com/viant/assertly" packages = ["."] pruneopts = "" revision = "04f45e0aeb6f3455884877b047a97bcc95dc9493" version = "v0.4.8" [[projects]] digest = "1:5913451bc2d274673c0716efe226a137625740cd9380641f4d8300ff4f2d82a0" name = "github.com/viant/toolbox" packages = [ ".", "cred", "data", "storage", "url", ] pruneopts = "" revision = "1be8e4d172138324f40d55ea61a2aeab0c5ce864" version = "v0.24.0" [[projects]] branch = "master" digest = "1:9d150270ca2c3356f2224a0878daa1652e4d0b25b345f18b4f6e156cc4b8ec5e" name = "golang.org/x/crypto" packages = [ "blowfish", "curve25519", "ed25519", "ed25519/internal/edwards25519", "internal/chacha20", "internal/subtle", "poly1305", "ssh", ] pruneopts = "" revision = "f99c8df09eb5bff426315721bfa5f16a99cad32c" [[projects]] branch = "master" digest = "1:5a56f211e7c12a65c5585c629457a2fb91d8719844ee8fab92727ea8adb5721c" name = "golang.org/x/net" packages = [ "context", "context/ctxhttp", "websocket", ] pruneopts = "" revision = "461777fb6f67e8cb9d70cda16573678d085a74cf" [[projects]] branch = "master" digest = "1:01bdbbc604dcd5afb6f66a717f69ad45e9643c72d5bc11678d44ffa5c50f9e42" name = "golang.org/x/oauth2" packages = [ ".", "google", "internal", "jws", "jwt", ] pruneopts = "" revision = "0f29369cfe4552d0e4bcddc57cc75f4d7e672a33" [[projects]] branch = "master" digest = "1:8ddb956f67d4c176abbbc42b7514aaeaf9ea30daa24e27d2cf30ad82f9334a2c" name = "golang.org/x/sys" packages = ["cpu"] pruneopts = "" revision = "1e42afee0f762ed3d76e6dd942e4181855fd1849" [[projects]] digest = "1:47f391ee443f578f01168347818cb234ed819521e49e4d2c8dd2fb80d48ee41a" name = "google.golang.org/appengine" packages = [ ".", "internal", "internal/app_identity", "internal/base", "internal/datastore", "internal/log", "internal/modules", "internal/remote_api", "internal/urlfetch", "urlfetch", ] pruneopts = "" revision = "b2f4a3cf3c67576a2ee09e1fe62656a5086ce880" version = "v1.6.1" [[projects]] digest = "1:cedccf16b71e86db87a24f8d4c70b0a855872eb967cb906a66b95de56aefbd0d" name = "gopkg.in/yaml.v2" packages = ["."] pruneopts = "" revision = "51d6538a90f86fe93ac480b35f37b2be17fef232" version = "v2.2.2" [solve-meta] analyzer-name = "dep" analyzer-version = 1 input-imports = [ "github.com/go-errors/errors", "github.com/mailru/easyjson", "github.com/mailru/easyjson/jlexer", "github.com/mailru/easyjson/jwriter", "github.com/viant/assertly", "github.com/viant/toolbox", "github.com/viant/toolbox/url", "golang.org/x/net/websocket", ] solver-name = "gps-cdcl" solver-version = 1 golang-github-francoispqt-gojay-1.2.13/Gopkg.toml000066400000000000000000000013011365331665100217330ustar00rootroot00000000000000# Gopkg.toml example # # Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md # for detailed Gopkg.toml documentation. # # required = ["github.com/user/thing/cmd/thing"] # ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] # # [[constraint]] # name = "github.com/user/project" # version = "1.0.0" # # [[constraint]] # name = "github.com/user/project2" # branch = "dev" # source = "github.com/myfork/project2" # # [[override]] # name = "github.com/x/y" # version = "2.4.0" ignored = ["github.com/francoispqt/benchmarks*","github.com/stretchr/testify*","github.com/stretchr/testify","github.com/json-iterator/go","github.com/buger/jsonparser"] golang-github-francoispqt-gojay-1.2.13/LICENSE000066400000000000000000000020451365331665100210020ustar00rootroot00000000000000MIT License Copyright (c) 2016 gojay 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: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 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.golang-github-francoispqt-gojay-1.2.13/Makefile000066400000000000000000000002751365331665100214400ustar00rootroot00000000000000.PHONY: test test: go test -race -run=^Test -v .PHONY: cover cover: go test -coverprofile=coverage.out -covermode=atomic .PHONY: coverhtml coverhtml: go tool cover -html=coverage.outgolang-github-francoispqt-gojay-1.2.13/README.md000066400000000000000000000614241365331665100212620ustar00rootroot00000000000000[![Build Status](https://travis-ci.org/francoispqt/gojay.svg?branch=master)](https://travis-ci.org/francoispqt/gojay) [![codecov](https://codecov.io/gh/francoispqt/gojay/branch/master/graph/badge.svg)](https://codecov.io/gh/francoispqt/gojay) [![Go Report Card](https://goreportcard.com/badge/github.com/francoispqt/gojay)](https://goreportcard.com/report/github.com/francoispqt/gojay) [![Go doc](http://img.shields.io/badge/go-documentation-blue.svg?style=flat-square )](https://godoc.org/github.com/francoispqt/gojay) ![MIT License](https://img.shields.io/badge/license-mit-blue.svg?style=flat-square) [![Sourcegraph](https://sourcegraph.com/github.com/francoispqt/gojay/-/badge.svg)](https://sourcegraph.com/github.com/francoispqt/gojay) ![stability-stable](https://img.shields.io/badge/stability-stable-green.svg) # GoJay GoJay is a performant JSON encoder/decoder for Golang (currently the most performant, [see benchmarks](#benchmark-results)). It has a simple API and doesn't use reflection. It relies on small interfaces to decode/encode structures and slices. Gojay also comes with powerful stream decoding features and an even faster [Unsafe](#unsafe-api) API. There is also a [code generation tool](https://github.com/francoispqt/gojay/tree/master/gojay) to make usage easier and faster. # Why another JSON parser? I looked at other fast decoder/encoder and realised it was mostly hardly readable static code generation or a lot of reflection, poor streaming features, and not so fast in the end. Also, I wanted to build a decoder that could consume an io.Reader of line or comma delimited JSON, in a JIT way. To consume a flow of JSON objects from a TCP connection for example or from a standard output. Same way I wanted to build an encoder that could encode a flow of data to a io.Writer. This is how GoJay aims to be a very fast, JIT stream parser with 0 reflection, low allocation with a friendly API. # Get started ```bash go get github.com/francoispqt/gojay ``` * [Encoder](#encoding) * [Decoder](#decoding) * [Stream API](#stream-api) * [Code Generation](https://github.com/francoispqt/gojay/tree/master/gojay) ## Decoding Decoding is done through two different API similar to standard `encoding/json`: * [Unmarshal](#unmarshal-api) * [Decode](#decode-api) Example of basic stucture decoding with Unmarshal: ```go import "github.com/francoispqt/gojay" type user struct { id int name string email string } // implement gojay.UnmarshalerJSONObject func (u *user) UnmarshalJSONObject(dec *gojay.Decoder, key string) error { switch key { case "id": return dec.Int(&u.id) case "name": return dec.String(&u.name) case "email": return dec.String(&u.email) } return nil } func (u *user) NKeys() int { return 3 } func main() { u := &user{} d := []byte(`{"id":1,"name":"gojay","email":"gojay@email.com"}`) err := gojay.UnmarshalJSONObject(d, u) if err != nil { log.Fatal(err) } } ``` with Decode: ```go func main() { u := &user{} dec := gojay.NewDecoder(bytes.NewReader([]byte(`{"id":1,"name":"gojay","email":"gojay@email.com"}`))) err := dec.DecodeObject(d, u) if err != nil { log.Fatal(err) } } ``` ### Unmarshal API Unmarshal API decodes a `[]byte` to a given pointer with a single function. Behind the doors, Unmarshal API borrows a `*gojay.Decoder` resets its settings and decodes the data to the given pointer and releases the `*gojay.Decoder` to the pool when it finishes, whether it encounters an error or not. If it cannot find the right Decoding strategy for the type of the given pointer, it returns an `InvalidUnmarshalError`. You can test the error returned by doing `if ok := err.(InvalidUnmarshalError); ok {}`. Unmarshal API comes with three functions: * Unmarshal ```go func Unmarshal(data []byte, v interface{}) error ``` * UnmarshalJSONObject ```go func UnmarshalJSONObject(data []byte, v gojay.UnmarshalerJSONObject) error ``` * UnmarshalJSONArray ```go func UnmarshalJSONArray(data []byte, v gojay.UnmarshalerJSONArray) error ``` ### Decode API Decode API decodes a `[]byte` to a given pointer by creating or borrowing a `*gojay.Decoder` with an `io.Reader` and calling `Decode` methods. __Getting a *gojay.Decoder or Borrowing__ You can either get a fresh `*gojay.Decoder` calling `dec := gojay.NewDecoder(io.Reader)` or borrow one from the pool by calling `dec := gojay.BorrowDecoder(io.Reader)`. After using a decoder, you can release it by calling `dec.Release()`. Beware, if you reuse the decoder after releasing it, it will panic with an error of type `InvalidUsagePooledDecoderError`. If you want to fully benefit from the pooling, you must release your decoders after using. Example getting a fresh an releasing: ```go str := "" dec := gojay.NewDecoder(strings.NewReader(`"test"`)) defer dec.Release() if err := dec.Decode(&str); err != nil { log.Fatal(err) } ``` Example borrowing a decoder and releasing: ```go str := "" dec := gojay.BorrowDecoder(strings.NewReader(`"test"`)) defer dec.Release() if err := dec.Decode(&str); err != nil { log.Fatal(err) } ``` `*gojay.Decoder` has multiple methods to decode to specific types: * Decode ```go func (dec *gojay.Decoder) Decode(v interface{}) error ``` * DecodeObject ```go func (dec *gojay.Decoder) DecodeObject(v gojay.UnmarshalerJSONObject) error ``` * DecodeArray ```go func (dec *gojay.Decoder) DecodeArray(v gojay.UnmarshalerJSONArray) error ``` * DecodeInt ```go func (dec *gojay.Decoder) DecodeInt(v *int) error ``` * DecodeBool ```go func (dec *gojay.Decoder) DecodeBool(v *bool) error ``` * DecodeString ```go func (dec *gojay.Decoder) DecodeString(v *string) error ``` All DecodeXxx methods are used to decode top level JSON values. If you are decoding keys or items of a JSON object or array, don't use the Decode methods. Example: ```go reader := strings.NewReader(`"John Doe"`) dec := NewDecoder(reader) var str string err := dec.DecodeString(&str) if err != nil { log.Fatal(err) } fmt.Println(str) // John Doe ``` ### Structs and Maps #### UnmarshalerJSONObject Interface To unmarshal a JSON object to a structure, the structure must implement the `UnmarshalerJSONObject` interface: ```go type UnmarshalerJSONObject interface { UnmarshalJSONObject(*gojay.Decoder, string) error NKeys() int } ``` `UnmarshalJSONObject` method takes two arguments, the first one is a pointer to the Decoder (*gojay.Decoder) and the second one is the string value of the current key being parsed. If the JSON data is not an object, the UnmarshalJSONObject method will never be called. `NKeys` method must return the number of keys to Unmarshal in the JSON object or 0. If zero is returned, all keys will be parsed. Example of implementation for a struct: ```go type user struct { id int name string email string } // implement UnmarshalerJSONObject func (u *user) UnmarshalJSONObject(dec *gojay.Decoder, key string) error { switch key { case "id": return dec.Int(&u.id) case "name": return dec.String(&u.name) case "email": return dec.String(&u.email) } return nil } func (u *user) NKeys() int { return 3 } ``` Example of implementation for a `map[string]string`: ```go // define our custom map type implementing UnmarshalerJSONObject type message map[string]string // Implementing Unmarshaler func (m message) UnmarshalJSONObject(dec *gojay.Decoder, k string) error { str := "" err := dec.String(&str) if err != nil { return err } m[k] = str return nil } // we return 0, it tells the Decoder to decode all keys func (m message) NKeys() int { return 0 } ``` ### Arrays, Slices and Channels To unmarshal a JSON object to a slice an array or a channel, it must implement the UnmarshalerJSONArray interface: ```go type UnmarshalerJSONArray interface { UnmarshalJSONArray(*gojay.Decoder) error } ``` UnmarshalJSONArray method takes one argument, a pointer to the Decoder (*gojay.Decoder). If the JSON data is not an array, the Unmarshal method will never be called. Example of implementation with a slice: ```go type testSlice []string // implement UnmarshalerJSONArray func (t *testSlice) UnmarshalJSONArray(dec *gojay.Decoder) error { str := "" if err := dec.String(&str); err != nil { return err } *t = append(*t, str) return nil } func main() { dec := gojay.BorrowDecoder(strings.NewReader(`["Tom", "Jim"]`)) var slice testSlice err := dec.DecodeArray(&slice) if err != nil { log.Fatal(err) } fmt.Println(slice) // [Tom Jim] dec.Release() } ``` Example of implementation with a channel: ```go type testChannel chan string // implement UnmarshalerJSONArray func (c testChannel) UnmarshalJSONArray(dec *gojay.Decoder) error { str := "" if err := dec.String(&str); err != nil { return err } c <- str return nil } func main() { dec := gojay.BorrowDecoder(strings.NewReader(`["Tom", "Jim"]`)) c := make(testChannel, 2) err := dec.DecodeArray(c) if err != nil { log.Fatal(err) } for i := 0; i < 2; i++ { fmt.Println(<-c) } close(c) dec.Release() } ``` Example of implementation with an array: ```go type testArray [3]string // implement UnmarshalerJSONArray func (a *testArray) UnmarshalJSONArray(dec *Decoder) error { var str string if err := dec.String(&str); err != nil { return err } a[dec.Index()] = str return nil } func main() { dec := gojay.BorrowDecoder(strings.NewReader(`["Tom", "Jim", "Bob"]`)) var a testArray err := dec.DecodeArray(&a) fmt.Println(a) // [Tom Jim Bob] dec.Release() } ``` ### Other types To decode other types (string, int, int32, int64, uint32, uint64, float, booleans), you don't need to implement any interface. Example of encoding strings: ```go func main() { json := []byte(`"Jay"`) var v string err := gojay.Unmarshal(json, &v) if err != nil { log.Fatal(err) } fmt.Println(v) // Jay } ``` ### Decode values methods When decoding a JSON object of a JSON array using `UnmarshalerJSONObject` or `UnmarshalerJSONArray` interface, the `gojay.Decoder` provides dozens of methods to Decode multiple types. Non exhaustive list of methods available (to see all methods, check the godoc): ```go dec.Int dec.Int8 dec.Int16 dec.Int32 dec.Int64 dec.Uint8 dec.Uint16 dec.Uint32 dec.Uint64 dec.String dec.Time dec.Bool dec.SQLNullString dec.SQLNullInt64 ``` ## Encoding Encoding is done through two different API similar to standard `encoding/json`: * [Marshal](#marshal-api) * [Encode](#encode-api) Example of basic structure encoding with Marshal: ```go import "github.com/francoispqt/gojay" type user struct { id int name string email string } // implement MarshalerJSONObject func (u *user) MarshalJSONObject(enc *gojay.Encoder) { enc.IntKey("id", u.id) enc.StringKey("name", u.name) enc.StringKey("email", u.email) } func (u *user) IsNil() bool { return u == nil } func main() { u := &user{1, "gojay", "gojay@email.com"} b, err := gojay.MarshalJSONObject(u) if err != nil { log.Fatal(err) } fmt.Println(string(b)) // {"id":1,"name":"gojay","email":"gojay@email.com"} } ``` with Encode: ```go func main() { u := &user{1, "gojay", "gojay@email.com"} b := strings.Builder{} enc := gojay.NewEncoder(&b) if err := enc.Encode(u); err != nil { log.Fatal(err) } fmt.Println(b.String()) // {"id":1,"name":"gojay","email":"gojay@email.com"} } ``` ### Marshal API Marshal API encodes a value to a JSON `[]byte` with a single function. Behind the doors, Marshal API borrows a `*gojay.Encoder` resets its settings and encodes the data to an internal byte buffer and releases the `*gojay.Encoder` to the pool when it finishes, whether it encounters an error or not. If it cannot find the right Encoding strategy for the type of the given value, it returns an `InvalidMarshalError`. You can test the error returned by doing `if ok := err.(InvalidMarshalError); ok {}`. Marshal API comes with three functions: * Marshal ```go func Marshal(v interface{}) ([]byte, error) ``` * MarshalJSONObject ```go func MarshalJSONObject(v gojay.MarshalerJSONObject) ([]byte, error) ``` * MarshalJSONArray ```go func MarshalJSONArray(v gojay.MarshalerJSONArray) ([]byte, error) ``` ### Encode API Encode API decodes a value to JSON by creating or borrowing a `*gojay.Encoder` sending it to an `io.Writer` and calling `Encode` methods. __Getting a *gojay.Encoder or Borrowing__ You can either get a fresh `*gojay.Encoder` calling `enc := gojay.NewEncoder(io.Writer)` or borrow one from the pool by calling `enc := gojay.BorrowEncoder(io.Writer)`. After using an encoder, you can release it by calling `enc.Release()`. Beware, if you reuse the encoder after releasing it, it will panic with an error of type `InvalidUsagePooledEncoderError`. If you want to fully benefit from the pooling, you must release your encoders after using. Example getting a fresh encoder an releasing: ```go str := "test" b := strings.Builder{} enc := gojay.NewEncoder(&b) defer enc.Release() if err := enc.Encode(str); err != nil { log.Fatal(err) } ``` Example borrowing an encoder and releasing: ```go str := "test" b := strings.Builder{} enc := gojay.BorrowEncoder(b) defer enc.Release() if err := enc.Encode(str); err != nil { log.Fatal(err) } ``` `*gojay.Encoder` has multiple methods to encoder specific types to JSON: * Encode ```go func (enc *gojay.Encoder) Encode(v interface{}) error ``` * EncodeObject ```go func (enc *gojay.Encoder) EncodeObject(v gojay.MarshalerJSONObject) error ``` * EncodeArray ```go func (enc *gojay.Encoder) EncodeArray(v gojay.MarshalerJSONArray) error ``` * EncodeInt ```go func (enc *gojay.Encoder) EncodeInt(n int) error ``` * EncodeInt64 ```go func (enc *gojay.Encoder) EncodeInt64(n int64) error ``` * EncodeFloat ```go func (enc *gojay.Encoder) EncodeFloat(n float64) error ``` * EncodeBool ```go func (enc *gojay.Encoder) EncodeBool(v bool) error ``` * EncodeString ```go func (enc *gojay.Encoder) EncodeString(s string) error ``` ### Structs and Maps To encode a structure, the structure must implement the MarshalerJSONObject interface: ```go type MarshalerJSONObject interface { MarshalJSONObject(enc *gojay.Encoder) IsNil() bool } ``` `MarshalJSONObject` method takes one argument, a pointer to the Encoder (*gojay.Encoder). The method must add all the keys in the JSON Object by calling Decoder's methods. IsNil method returns a boolean indicating if the interface underlying value is nil or not. It is used to safely ensure that the underlying value is not nil without using Reflection. Example of implementation for a struct: ```go type user struct { id int name string email string } // implement MarshalerJSONObject func (u *user) MarshalJSONObject(enc *gojay.Encoder) { enc.IntKey("id", u.id) enc.StringKey("name", u.name) enc.StringKey("email", u.email) } func (u *user) IsNil() bool { return u == nil } ``` Example of implementation for a `map[string]string`: ```go // define our custom map type implementing MarshalerJSONObject type message map[string]string // Implementing Marshaler func (m message) MarshalJSONObject(enc *gojay.Encoder) { for k, v := range m { enc.StringKey(k, v) } } func (m message) IsNil() bool { return m == nil } ``` ### Arrays and Slices To encode an array or a slice, the slice/array must implement the MarshalerJSONArray interface: ```go type MarshalerJSONArray interface { MarshalJSONArray(enc *gojay.Encoder) IsNil() bool } ``` `MarshalJSONArray` method takes one argument, a pointer to the Encoder (*gojay.Encoder). The method must add all element in the JSON Array by calling Decoder's methods. `IsNil` method returns a boolean indicating if the interface underlying value is nil(empty) or not. It is used to safely ensure that the underlying value is not nil without using Reflection and also to in `OmitEmpty` feature. Example of implementation: ```go type users []*user // implement MarshalerJSONArray func (u *users) MarshalJSONArray(enc *gojay.Encoder) { for _, e := range u { enc.Object(e) } } func (u *users) IsNil() bool { return len(u) == 0 } ``` ### Other types To encode other types (string, int, float, booleans), you don't need to implement any interface. Example of encoding strings: ```go func main() { name := "Jay" b, err := gojay.Marshal(name) if err != nil { log.Fatal(err) } fmt.Println(string(b)) // "Jay" } ``` # Stream API ### Stream Decoding GoJay ships with a powerful stream decoder. It allows to read continuously from an io.Reader stream and do JIT decoding writing unmarshalled JSON to a channel to allow async consuming. When using the Stream API, the Decoder implements context.Context to provide graceful cancellation. To decode a stream of JSON, you must call `gojay.Stream.DecodeStream` and pass it a `UnmarshalerStream` implementation. ```go type UnmarshalerStream interface { UnmarshalStream(*StreamDecoder) error } ``` Example of implementation of stream reading from a WebSocket connection: ```go // implement UnmarshalerStream type ChannelStream chan *user func (c ChannelStream) UnmarshalStream(dec *gojay.StreamDecoder) error { u := &user{} if err := dec.Object(u); err != nil { return err } c <- u return nil } func main() { // get our websocket connection origin := "http://localhost/" url := "ws://localhost:12345/ws" ws, err := websocket.Dial(url, "", origin) if err != nil { log.Fatal(err) } // create our channel which will receive our objects streamChan := ChannelStream(make(chan *user)) // borrow a decoder dec := gojay.Stream.BorrowDecoder(ws) // start decoding, it will block until a JSON message is decoded from the WebSocket // or until Done channel is closed go dec.DecodeStream(streamChan) for { select { case v := <-streamChan: // Got something from my websocket! log.Println(v) case <-dec.Done(): log.Println("finished reading from WebSocket") os.Exit(0) } } } ``` ### Stream Encoding GoJay ships with a powerful stream encoder part of the Stream API. It allows to write continuously to an io.Writer and do JIT encoding of data fed to a channel to allow async consuming. You can set multiple consumers on the channel to be as performant as possible. Consumers are non blocking and are scheduled individually in their own go routine. When using the Stream API, the Encoder implements context.Context to provide graceful cancellation. To encode a stream of data, you must call `EncodeStream` and pass it a `MarshalerStream` implementation. ```go type MarshalerStream interface { MarshalStream(enc *gojay.StreamEncoder) } ``` Example of implementation of stream writing to a WebSocket: ```go // Our structure which will be pushed to our stream type user struct { id int name string email string } func (u *user) MarshalJSONObject(enc *gojay.Encoder) { enc.IntKey("id", u.id) enc.StringKey("name", u.name) enc.StringKey("email", u.email) } func (u *user) IsNil() bool { return u == nil } // Our MarshalerStream implementation type StreamChan chan *user func (s StreamChan) MarshalStream(enc *gojay.StreamEncoder) { select { case <-enc.Done(): return case o := <-s: enc.Object(o) } } // Our main function func main() { // get our websocket connection origin := "http://localhost/" url := "ws://localhost:12345/ws" ws, err := websocket.Dial(url, "", origin) if err != nil { log.Fatal(err) } // we borrow an encoder set stdout as the writer, // set the number of consumer to 10 // and tell the encoder to separate each encoded element // added to the channel by a new line character enc := gojay.Stream.BorrowEncoder(ws).NConsumer(10).LineDelimited() // instantiate our MarshalerStream s := StreamChan(make(chan *user)) // start the stream encoder // will block its goroutine until enc.Cancel(error) is called // or until something is written to the channel go enc.EncodeStream(s) // write to our MarshalerStream for i := 0; i < 1000; i++ { s <- &user{i, "username", "user@email.com"} } // Wait <-enc.Done() } ``` # Unsafe API Unsafe API has the same functions than the regular API, it only has `Unmarshal API` for now. It is unsafe because it makes assumptions on the quality of the given JSON. If you are not sure if your JSON is valid, don't use the Unsafe API. Also, the `Unsafe` API does not copy the buffer when using Unmarshal API, which, in case of string decoding, can lead to data corruption if a byte buffer is reused. Using the `Decode` API makes `Unsafe` API safer as the io.Reader relies on `copy` builtin method and `Decoder` will have its own internal buffer :) Access the `Unsafe` API this way: ```go gojay.Unsafe.Unmarshal(b, v) ``` # Benchmarks Benchmarks encode and decode three different data based on size (small, medium, large). To run benchmark for decoder: ```bash cd $GOPATH/src/github.com/francoispqt/gojay/benchmarks/decoder && make bench ``` To run benchmark for encoder: ```bash cd $GOPATH/src/github.com/francoispqt/gojay/benchmarks/encoder && make bench ``` # Benchmark Results ## Decode ### Small Payload [benchmark code is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/decoder/decoder_bench_small_test.go) [benchmark data is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/benchmarks_small.go) | | ns/op | bytes/op | allocs/op | |-----------------|-----------|--------------|-----------| | Std Library | 2547 | 496 | 4 | | JsonIter | 2046 | 312 | 12 | | JsonParser | 1408 | 0 | 0 | | EasyJson | 929 | 240 | 2 | | **GoJay** | **807** | **256** | **2** | | **GoJay-unsafe**| **712** | **112** | **1** | ### Medium Payload [benchmark code is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/decoder/decoder_bench_medium_test.go) [benchmark data is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/benchmarks_medium.go) | | ns/op | bytes/op | allocs/op | |-----------------|-----------|----------|-----------| | Std Library | 30148 | 2152 | 496 | | JsonIter | 16309 | 2976 | 80 | | JsonParser | 7793 | 0 | 0 | | EasyJson | 7957 | 232 | 6 | | **GoJay** | **4984** | **2448** | **8** | | **GoJay-unsafe**| **4809** | **144** | **7** | ### Large Payload [benchmark code is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/decoder/decoder_bench_large_test.go) [benchmark data is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/benchmarks_large.go) | | ns/op | bytes/op | allocs/op | |-----------------|-----------|-------------|-----------| | JsonIter | 210078 | 41712 | 1136 | | EasyJson | 106626 | 160 | 2 | | JsonParser | 66813 | 0 | 0 | | **GoJay** | **52153** | **31241** | **77** | | **GoJay-unsafe**| **48277** | **2561** | **76** | ## Encode ### Small Struct [benchmark code is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/encoder/encoder_bench_small_test.go) [benchmark data is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/benchmarks_small.go) | | ns/op | bytes/op | allocs/op | |----------------|----------|--------------|-----------| | Std Library | 1280 | 464 | 3 | | EasyJson | 871 | 944 | 6 | | JsonIter | 866 | 272 | 3 | | **GoJay** | **543** | **112** | **1** | | **GoJay-func** | **347** | **0** | **0** | ### Medium Struct [benchmark code is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/encoder/encoder_bench_medium_test.go) [benchmark data is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/benchmarks_medium.go) | | ns/op | bytes/op | allocs/op | |-------------|----------|--------------|-----------| | Std Library | 5006 | 1496 | 25 | | JsonIter | 2232 | 1544 | 20 | | EasyJson | 1997 | 1544 | 19 | | **GoJay** | **1522** | **312** | **14** | ### Large Struct [benchmark code is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/encoder/encoder_bench_large_test.go) [benchmark data is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/benchmarks_large.go) | | ns/op | bytes/op | allocs/op | |-------------|-----------|--------------|-----------| | Std Library | 66441 | 20576 | 332 | | JsonIter | 35247 | 20255 | 328 | | EasyJson | 32053 | 15474 | 327 | | **GoJay** | **27847** | **9802** | **318** | # Contributing Contributions are welcome :) If you encounter issues please report it in Github and/or send an email at [francois@parquet.ninja](mailto:francois@parquet.ninja) golang-github-francoispqt-gojay-1.2.13/benchmarks/000077500000000000000000000000001365331665100221115ustar00rootroot00000000000000golang-github-francoispqt-gojay-1.2.13/benchmarks/benchmarks_large.go000066400000000000000000000752061365331665100257410ustar00rootroot00000000000000package benchmarks import ( "strconv" "github.com/francoispqt/gojay" ) type DSUser struct { Username string } func (m *DSUser) UnmarshalJSONObject(dec *gojay.Decoder, key string) error { switch key { case "username": return dec.AddString(&m.Username) } return nil } func (m *DSUser) NKeys() int { return 1 } func (m *DSUser) IsNil() bool { return m == nil } func (m *DSUser) MarshalJSONObject(enc *gojay.Encoder) { enc.AddStringKey("username", m.Username) } type DSTopic struct { Id int Slug string } func (m *DSTopic) UnmarshalJSONObject(dec *gojay.Decoder, key string) error { switch key { case "id": return dec.AddInt(&m.Id) case "slug": return dec.AddString(&m.Slug) } return nil } func (m *DSTopic) NKeys() int { return 2 } func (m *DSTopic) IsNil() bool { return m == nil } func (m *DSTopic) MarshalJSONObject(enc *gojay.Encoder) { enc.AddIntKey("id", m.Id) enc.AddStringKey("slug", m.Slug) } type DSTopics []*DSTopic func (t *DSTopics) UnmarshalJSONArray(dec *gojay.Decoder) error { dsTopic := &DSTopic{} *t = append(*t, dsTopic) return dec.AddObject(dsTopic) } func (m *DSTopics) MarshalJSONArray(enc *gojay.Encoder) { for _, e := range *m { enc.AddObject(e) } } func (m *DSTopics) IsNil() bool { return m == nil } type DSTopicsList struct { Topics DSTopics MoreTopicsUrl string } func (m *DSTopicsList) UnmarshalJSONObject(dec *gojay.Decoder, key string) error { switch key { case "topics": m.Topics = DSTopics{} return dec.AddArray(&m.Topics) case "more_topics_url": return dec.AddString(&m.MoreTopicsUrl) } return nil } func (m *DSTopicsList) NKeys() int { return 2 } func (m *DSTopicsList) IsNil() bool { return m == nil } func (m *DSTopicsList) MarshalJSONObject(enc *gojay.Encoder) { enc.AddArrayKey("users", &m.Topics) enc.AddStringKey("more_topics_url", m.MoreTopicsUrl) } type DSUsers []*DSUser func (t *DSUsers) UnmarshalJSONArray(dec *gojay.Decoder) error { dsUser := DSUser{} *t = append(*t, &dsUser) return dec.AddObject(&dsUser) } func (m *DSUsers) MarshalJSONArray(enc *gojay.Encoder) { for _, e := range *m { enc.AddObject(e) } } func (m *DSUsers) IsNil() bool { return m == nil } type LargePayload struct { Users DSUsers Topics *DSTopicsList } func (m *LargePayload) UnmarshalJSONObject(dec *gojay.Decoder, key string) error { switch key { case "users": return dec.AddArray(&m.Users) case "topics": m.Topics = &DSTopicsList{} return dec.AddObject(m.Topics) } return nil } func (m *LargePayload) NKeys() int { return 2 } //easyjson:json func (m *LargePayload) MarshalJSONObject(enc *gojay.Encoder) { enc.AddArrayKey("users", &m.Users) enc.AddObjectKey("topics", m.Topics) } func (m *LargePayload) IsNil() bool { return m == nil } var LargeFixture = []byte(` {"users":[{"id":-1,"username":"system","avatar_template":"/user_avatar/discourse.metabase.com/system/{size}/6_1.png"},{"id":89,"username":"zergot","avatar_template":"https://avatars.discourse.org/v2/letter/z/0ea827/{size}.png"},{"id":1,"username":"sameer","avatar_template":"https://avatars.discourse.org/v2/letter/s/bbce88/{size}.png"},{"id":84,"username":"HenryMirror","avatar_template":"https://avatars.discourse.org/v2/letter/h/ecd19e/{size}.png"},{"id":73,"username":"fimp","avatar_template":"https://avatars.discourse.org/v2/letter/f/ee59a6/{size}.png"},{"id":14,"username":"agilliland","avatar_template":"/user_avatar/discourse.metabase.com/agilliland/{size}/26_1.png"},{"id":87,"username":"amir","avatar_template":"https://avatars.discourse.org/v2/letter/a/c37758/{size}.png"},{"id":82,"username":"waseem","avatar_template":"https://avatars.discourse.org/v2/letter/w/9dc877/{size}.png"},{"id":78,"username":"tovenaar","avatar_template":"https://avatars.discourse.org/v2/letter/t/9de0a6/{size}.png"},{"id":74,"username":"Ben","avatar_template":"https://avatars.discourse.org/v2/letter/b/df788c/{size}.png"},{"id":71,"username":"MarkLaFay","avatar_template":"https://avatars.discourse.org/v2/letter/m/3bc359/{size}.png"},{"id":72,"username":"camsaul","avatar_template":"/user_avatar/discourse.metabase.com/camsaul/{size}/70_1.png"},{"id":53,"username":"mhjb","avatar_template":"/user_avatar/discourse.metabase.com/mhjb/{size}/54_1.png"},{"id":58,"username":"jbwiv","avatar_template":"https://avatars.discourse.org/v2/letter/j/6bbea6/{size}.png"},{"id":70,"username":"Maggs","avatar_template":"https://avatars.discourse.org/v2/letter/m/bbce88/{size}.png"},{"id":69,"username":"andrefaria","avatar_template":"/user_avatar/discourse.metabase.com/andrefaria/{size}/65_1.png"},{"id":60,"username":"bencarter78","avatar_template":"/user_avatar/discourse.metabase.com/bencarter78/{size}/59_1.png"},{"id":55,"username":"vikram","avatar_template":"https://avatars.discourse.org/v2/letter/v/e47774/{size}.png"},{"id":68,"username":"edchan77","avatar_template":"/user_avatar/discourse.metabase.com/edchan77/{size}/66_1.png"},{"id":9,"username":"karthikd","avatar_template":"https://avatars.discourse.org/v2/letter/k/cab0a1/{size}.png"},{"id":23,"username":"arthurz","avatar_template":"/user_avatar/discourse.metabase.com/arthurz/{size}/32_1.png"},{"id":3,"username":"tom","avatar_template":"/user_avatar/discourse.metabase.com/tom/{size}/21_1.png"},{"id":50,"username":"LeoNogueira","avatar_template":"/user_avatar/discourse.metabase.com/leonogueira/{size}/52_1.png"},{"id":66,"username":"ss06vi","avatar_template":"https://avatars.discourse.org/v2/letter/s/3ab097/{size}.png"},{"id":34,"username":"mattcollins","avatar_template":"/user_avatar/discourse.metabase.com/mattcollins/{size}/41_1.png"},{"id":51,"username":"krmmalik","avatar_template":"/user_avatar/discourse.metabase.com/krmmalik/{size}/53_1.png"},{"id":46,"username":"odysseas","avatar_template":"https://avatars.discourse.org/v2/letter/o/5f8ce5/{size}.png"},{"id":5,"username":"jonthewayne","avatar_template":"/user_avatar/discourse.metabase.com/jonthewayne/{size}/18_1.png"},{"id":11,"username":"anandiyer","avatar_template":"/user_avatar/discourse.metabase.com/anandiyer/{size}/23_1.png"},{"id":25,"username":"alnorth","avatar_template":"/user_avatar/discourse.metabase.com/alnorth/{size}/34_1.png"},{"id":52,"username":"j_at_svg","avatar_template":"https://avatars.discourse.org/v2/letter/j/96bed5/{size}.png"},{"id":42,"username":"styts","avatar_template":"/user_avatar/discourse.metabase.com/styts/{size}/47_1.png"}],"topics":{"can_create_topic":false,"more_topics_url":"/c/uncategorized/l/latest?page=1","draft":null,"draft_key":"new_topic","draft_sequence":null,"per_page":30,"topics":[{"id":8,"title":"Welcome to Metabase's Discussion Forum","fancy_title":"Welcome to Metabase’s Discussion Forum","slug":"welcome-to-metabases-discussion-forum","posts_count":1,"reply_count":0,"highest_post_number":1,"image_url":"/images/welcome/discourse-edit-post-animated.gif","created_at":"2015-10-17T00:14:49.526Z","last_posted_at":"2015-10-17T00:14:49.557Z","bumped":true,"bumped_at":"2015-10-21T02:32:22.486Z","unseen":false,"pinned":true,"unpinned":null,"excerpt":"Welcome to Metabase's discussion forum. This is a place to get help on installation, setting up as well as sharing tips and tricks.","visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"views":197,"like_count":0,"has_summary":false,"archetype":"regular","last_poster_username":"system","category_id":1,"pinned_globally":true,"posters":[{"extras":"latest single","description":"Original Poster, Most Recent Poster","user_id":-1}]},{"id":169,"title":"Formatting Dates","fancy_title":"Formatting Dates","slug":"formatting-dates","posts_count":1,"reply_count":0,"highest_post_number":1,"image_url":null,"created_at":"2016-01-14T06:30:45.311Z","last_posted_at":"2016-01-14T06:30:45.397Z","bumped":true,"bumped_at":"2016-01-14T06:30:45.397Z","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"views":11,"like_count":0,"has_summary":false,"archetype":"regular","last_poster_username":"zergot","category_id":1,"pinned_globally":false,"posters":[{"extras":"latest single","description":"Original Poster, Most Recent Poster","user_id":89}]},{"id":168,"title":"Setting for google api key","fancy_title":"Setting for google api key","slug":"setting-for-google-api-key","posts_count":2,"reply_count":0,"highest_post_number":2,"image_url":null,"created_at":"2016-01-13T17:14:31.799Z","last_posted_at":"2016-01-14T06:24:03.421Z","bumped":true,"bumped_at":"2016-01-14T06:24:03.421Z","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"views":16,"like_count":0,"has_summary":false,"archetype":"regular","last_poster_username":"zergot","category_id":1,"pinned_globally":false,"posters":[{"extras":"latest single","description":"Original Poster, Most Recent Poster","user_id":89}]},{"id":167,"title":"Cannot see non-US timezones on the admin","fancy_title":"Cannot see non-US timezones on the admin","slug":"cannot-see-non-us-timezones-on-the-admin","posts_count":1,"reply_count":0,"highest_post_number":1,"image_url":null,"created_at":"2016-01-13T17:07:36.764Z","last_posted_at":"2016-01-13T17:07:36.831Z","bumped":true,"bumped_at":"2016-01-13T17:07:36.831Z","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"views":11,"like_count":0,"has_summary":false,"archetype":"regular","last_poster_username":"zergot","category_id":1,"pinned_globally":false,"posters":[{"extras":"latest single","description":"Original Poster, Most Recent Poster","user_id":89}]},{"id":164,"title":"External (Metabase level) linkages in data schema","fancy_title":"External (Metabase level) linkages in data schema","slug":"external-metabase-level-linkages-in-data-schema","posts_count":4,"reply_count":1,"highest_post_number":4,"image_url":null,"created_at":"2016-01-11T13:51:02.286Z","last_posted_at":"2016-01-12T11:06:37.259Z","bumped":true,"bumped_at":"2016-01-12T11:06:37.259Z","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"views":32,"like_count":0,"has_summary":false,"archetype":"regular","last_poster_username":"zergot","category_id":1,"pinned_globally":false,"posters":[{"extras":"latest","description":"Original Poster, Most Recent Poster","user_id":89},{"extras":null,"description":"Frequent Poster","user_id":1}]},{"id":155,"title":"Query working on \"Questions\" but not in \"Pulses\"","fancy_title":"Query working on “Questions” but not in “Pulses”","slug":"query-working-on-questions-but-not-in-pulses","posts_count":3,"reply_count":0,"highest_post_number":3,"image_url":null,"created_at":"2016-01-01T14:06:10.083Z","last_posted_at":"2016-01-08T22:37:51.772Z","bumped":true,"bumped_at":"2016-01-08T22:37:51.772Z","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"views":72,"like_count":0,"has_summary":false,"archetype":"regular","last_poster_username":"agilliland","category_id":1,"pinned_globally":false,"posters":[{"extras":null,"description":"Original Poster","user_id":84},{"extras":null,"description":"Frequent Poster","user_id":73},{"extras":"latest","description":"Most Recent Poster","user_id":14}]},{"id":161,"title":"Pulses posted to Slack don't show question output","fancy_title":"Pulses posted to Slack don’t show question output","slug":"pulses-posted-to-slack-dont-show-question-output","posts_count":2,"reply_count":0,"highest_post_number":2,"image_url":"/uploads/default/original/1X/9d2806517bf3598b10be135b2c58923b47ba23e7.png","created_at":"2016-01-08T22:09:58.205Z","last_posted_at":"2016-01-08T22:28:44.685Z","bumped":true,"bumped_at":"2016-01-08T22:28:44.685Z","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"views":34,"like_count":0,"has_summary":false,"archetype":"regular","last_poster_username":"sameer","category_id":1,"pinned_globally":false,"posters":[{"extras":null,"description":"Original Poster","user_id":87},{"extras":"latest","description":"Most Recent Poster","user_id":1}]},{"id":152,"title":"Should we build Kafka connecter or Kafka plugin","fancy_title":"Should we build Kafka connecter or Kafka plugin","slug":"should-we-build-kafka-connecter-or-kafka-plugin","posts_count":4,"reply_count":1,"highest_post_number":4,"image_url":null,"created_at":"2015-12-28T20:37:23.501Z","last_posted_at":"2015-12-31T18:16:45.477Z","bumped":true,"bumped_at":"2015-12-31T18:16:45.477Z","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"views":84,"like_count":0,"has_summary":false,"archetype":"regular","last_poster_username":"sameer","category_id":1,"pinned_globally":false,"posters":[{"extras":null,"description":"Original Poster","user_id":82},{"extras":"latest","description":"Most Recent Poster, Frequent Poster","user_id":1}]},{"id":147,"title":"Change X and Y on graph","fancy_title":"Change X and Y on graph","slug":"change-x-and-y-on-graph","posts_count":1,"reply_count":0,"highest_post_number":1,"image_url":null,"created_at":"2015-12-21T17:52:46.581Z","last_posted_at":"2015-12-21T17:52:46.684Z","bumped":true,"bumped_at":"2015-12-21T18:19:13.003Z","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"views":68,"like_count":0,"has_summary":false,"archetype":"regular","last_poster_username":"tovenaar","category_id":1,"pinned_globally":false,"posters":[{"extras":"latest single","description":"Original Poster, Most Recent Poster","user_id":78}]},{"id":142,"title":"Issues sending mail via office365 relay","fancy_title":"Issues sending mail via office365 relay","slug":"issues-sending-mail-via-office365-relay","posts_count":5,"reply_count":2,"highest_post_number":5,"image_url":null,"created_at":"2015-12-16T10:38:47.315Z","last_posted_at":"2015-12-21T09:26:27.167Z","bumped":true,"bumped_at":"2015-12-21T09:26:27.167Z","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"views":122,"like_count":0,"has_summary":false,"archetype":"regular","last_poster_username":"Ben","category_id":1,"pinned_globally":false,"posters":[{"extras":"latest","description":"Original Poster, Most Recent Poster","user_id":74},{"extras":null,"description":"Frequent Poster","user_id":1}]},{"id":137,"title":"I see triplicates of my mongoDB collections","fancy_title":"I see triplicates of my mongoDB collections","slug":"i-see-triplicates-of-my-mongodb-collections","posts_count":3,"reply_count":0,"highest_post_number":3,"image_url":null,"created_at":"2015-12-14T13:33:03.426Z","last_posted_at":"2015-12-17T18:40:05.487Z","bumped":true,"bumped_at":"2015-12-17T18:40:05.487Z","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"views":97,"like_count":0,"has_summary":false,"archetype":"regular","last_poster_username":"MarkLaFay","category_id":1,"pinned_globally":false,"posters":[{"extras":"latest","description":"Original Poster, Most Recent Poster","user_id":71},{"extras":null,"description":"Frequent Poster","user_id":14}]},{"id":140,"title":"Google Analytics plugin","fancy_title":"Google Analytics plugin","slug":"google-analytics-plugin","posts_count":1,"reply_count":0,"highest_post_number":1,"image_url":null,"created_at":"2015-12-15T13:00:55.644Z","last_posted_at":"2015-12-15T13:00:55.705Z","bumped":true,"bumped_at":"2015-12-15T13:00:55.705Z","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"views":105,"like_count":0,"has_summary":false,"archetype":"regular","last_poster_username":"fimp","category_id":1,"pinned_globally":false,"posters":[{"extras":"latest single","description":"Original Poster, Most Recent Poster","user_id":73}]},{"id":138,"title":"With-mongo-connection failed: bad connection details:","fancy_title":"With-mongo-connection failed: bad connection details:","slug":"with-mongo-connection-failed-bad-connection-details","posts_count":1,"reply_count":0,"highest_post_number":1,"image_url":null,"created_at":"2015-12-14T17:28:11.041Z","last_posted_at":"2015-12-14T17:28:11.111Z","bumped":true,"bumped_at":"2015-12-14T17:28:11.111Z","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"views":56,"like_count":0,"has_summary":false,"archetype":"regular","last_poster_username":"MarkLaFay","category_id":1,"pinned_globally":false,"posters":[{"extras":"latest single","description":"Original Poster, Most Recent Poster","user_id":71}]},{"id":133,"title":"\"We couldn't understand your question.\" when I query mongoDB","fancy_title":"“We couldn’t understand your question.” when I query mongoDB","slug":"we-couldnt-understand-your-question-when-i-query-mongodb","posts_count":3,"reply_count":0,"highest_post_number":3,"image_url":null,"created_at":"2015-12-11T17:38:30.576Z","last_posted_at":"2015-12-14T13:31:26.395Z","bumped":true,"bumped_at":"2015-12-14T13:31:26.395Z","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"views":107,"like_count":0,"has_summary":false,"archetype":"regular","last_poster_username":"MarkLaFay","category_id":1,"pinned_globally":false,"posters":[{"extras":"latest","description":"Original Poster, Most Recent Poster","user_id":71},{"extras":null,"description":"Frequent Poster","user_id":72}]},{"id":129,"title":"My bar charts are all thin","fancy_title":"My bar charts are all thin","slug":"my-bar-charts-are-all-thin","posts_count":4,"reply_count":1,"highest_post_number":4,"image_url":"/uploads/default/original/1X/41bcf3b2a00dc7cfaff01cb3165d35d32a85bf1d.png","created_at":"2015-12-09T22:09:56.394Z","last_posted_at":"2015-12-11T19:00:45.289Z","bumped":true,"bumped_at":"2015-12-11T19:00:45.289Z","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"views":116,"like_count":0,"has_summary":false,"archetype":"regular","last_poster_username":"mhjb","category_id":1,"pinned_globally":false,"posters":[{"extras":"latest","description":"Original Poster, Most Recent Poster","user_id":53},{"extras":null,"description":"Frequent Poster","user_id":1}]},{"id":106,"title":"What is the expected return order of columns for graphing results when using raw SQL?","fancy_title":"What is the expected return order of columns for graphing results when using raw SQL?","slug":"what-is-the-expected-return-order-of-columns-for-graphing-results-when-using-raw-sql","posts_count":3,"reply_count":0,"highest_post_number":3,"image_url":null,"created_at":"2015-11-24T19:07:14.561Z","last_posted_at":"2015-12-11T17:04:14.149Z","bumped":true,"bumped_at":"2015-12-11T17:04:14.149Z","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"views":153,"like_count":0,"has_summary":false,"archetype":"regular","last_poster_username":"jbwiv","category_id":1,"pinned_globally":false,"posters":[{"extras":"latest","description":"Original Poster, Most Recent Poster","user_id":58},{"extras":null,"description":"Frequent Poster","user_id":14}]},{"id":131,"title":"Set site url from admin panel","fancy_title":"Set site url from admin panel","slug":"set-site-url-from-admin-panel","posts_count":2,"reply_count":0,"highest_post_number":2,"image_url":null,"created_at":"2015-12-10T06:22:46.042Z","last_posted_at":"2015-12-10T19:12:57.449Z","bumped":true,"bumped_at":"2015-12-10T19:12:57.449Z","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"views":77,"like_count":0,"has_summary":false,"archetype":"regular","last_poster_username":"sameer","category_id":1,"pinned_globally":false,"posters":[{"extras":null,"description":"Original Poster","user_id":70},{"extras":"latest","description":"Most Recent Poster","user_id":1}]},{"id":127,"title":"Internationalization (i18n)","fancy_title":"Internationalization (i18n)","slug":"internationalization-i18n","posts_count":2,"reply_count":0,"highest_post_number":2,"image_url":null,"created_at":"2015-12-08T16:55:37.397Z","last_posted_at":"2015-12-09T16:49:55.816Z","bumped":true,"bumped_at":"2015-12-09T16:49:55.816Z","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"views":85,"like_count":0,"has_summary":false,"archetype":"regular","last_poster_username":"agilliland","category_id":1,"pinned_globally":false,"posters":[{"extras":null,"description":"Original Poster","user_id":69},{"extras":"latest","description":"Most Recent Poster","user_id":14}]},{"id":109,"title":"Returning raw data with no filters always returns We couldn't understand your question","fancy_title":"Returning raw data with no filters always returns We couldn’t understand your question","slug":"returning-raw-data-with-no-filters-always-returns-we-couldnt-understand-your-question","posts_count":3,"reply_count":1,"highest_post_number":3,"image_url":null,"created_at":"2015-11-25T21:35:01.315Z","last_posted_at":"2015-12-09T10:26:12.255Z","bumped":true,"bumped_at":"2015-12-09T10:26:12.255Z","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"views":133,"like_count":0,"has_summary":false,"archetype":"regular","last_poster_username":"bencarter78","category_id":1,"pinned_globally":false,"posters":[{"extras":"latest","description":"Original Poster, Most Recent Poster","user_id":60},{"extras":null,"description":"Frequent Poster","user_id":14}]},{"id":103,"title":"Support for Cassandra?","fancy_title":"Support for Cassandra?","slug":"support-for-cassandra","posts_count":5,"reply_count":1,"highest_post_number":5,"image_url":null,"created_at":"2015-11-20T06:45:31.741Z","last_posted_at":"2015-12-09T03:18:51.274Z","bumped":true,"bumped_at":"2015-12-09T03:18:51.274Z","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"views":169,"like_count":0,"has_summary":false,"archetype":"regular","last_poster_username":"vikram","category_id":1,"pinned_globally":false,"posters":[{"extras":"latest","description":"Original Poster, Most Recent Poster","user_id":55},{"extras":null,"description":"Frequent Poster","user_id":1}]},{"id":128,"title":"Mongo query with Date breaks [solved: Mongo 3.0 required]","fancy_title":"Mongo query with Date breaks [solved: Mongo 3.0 required]","slug":"mongo-query-with-date-breaks-solved-mongo-3-0-required","posts_count":5,"reply_count":0,"highest_post_number":5,"image_url":null,"created_at":"2015-12-08T18:30:56.562Z","last_posted_at":"2015-12-08T21:03:02.421Z","bumped":true,"bumped_at":"2015-12-08T21:03:02.421Z","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"views":102,"like_count":1,"has_summary":false,"archetype":"regular","last_poster_username":"edchan77","category_id":1,"pinned_globally":false,"posters":[{"extras":"latest","description":"Original Poster, Most Recent Poster","user_id":68},{"extras":null,"description":"Frequent Poster","user_id":1}]},{"id":23,"title":"Can this connect to MS SQL Server?","fancy_title":"Can this connect to MS SQL Server?","slug":"can-this-connect-to-ms-sql-server","posts_count":7,"reply_count":1,"highest_post_number":7,"image_url":null,"created_at":"2015-10-21T18:52:37.987Z","last_posted_at":"2015-12-07T17:41:51.609Z","bumped":true,"bumped_at":"2015-12-07T17:41:51.609Z","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"views":367,"like_count":0,"has_summary":false,"archetype":"regular","last_poster_username":"sameer","category_id":1,"pinned_globally":false,"posters":[{"extras":null,"description":"Original Poster","user_id":9},{"extras":null,"description":"Frequent Poster","user_id":23},{"extras":null,"description":"Frequent Poster","user_id":3},{"extras":null,"description":"Frequent Poster","user_id":50},{"extras":"latest","description":"Most Recent Poster","user_id":1}]},{"id":121,"title":"Cannot restart metabase in docker","fancy_title":"Cannot restart metabase in docker","slug":"cannot-restart-metabase-in-docker","posts_count":5,"reply_count":1,"highest_post_number":5,"image_url":null,"created_at":"2015-12-04T21:28:58.137Z","last_posted_at":"2015-12-04T23:02:00.488Z","bumped":true,"bumped_at":"2015-12-04T23:02:00.488Z","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"views":96,"like_count":0,"has_summary":false,"archetype":"regular","last_poster_username":"sameer","category_id":1,"pinned_globally":false,"posters":[{"extras":null,"description":"Original Poster","user_id":66},{"extras":"latest","description":"Most Recent Poster, Frequent Poster","user_id":1}]},{"id":85,"title":"Edit Max Rows Count","fancy_title":"Edit Max Rows Count","slug":"edit-max-rows-count","posts_count":4,"reply_count":2,"highest_post_number":4,"image_url":null,"created_at":"2015-11-11T23:46:52.917Z","last_posted_at":"2015-11-24T01:01:14.569Z","bumped":true,"bumped_at":"2015-11-24T01:01:14.569Z","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"views":169,"like_count":1,"has_summary":false,"archetype":"regular","last_poster_username":"sameer","category_id":1,"pinned_globally":false,"posters":[{"extras":null,"description":"Original Poster","user_id":34},{"extras":"latest","description":"Most Recent Poster, Frequent Poster","user_id":1}]},{"id":96,"title":"Creating charts by querying more than one table at a time","fancy_title":"Creating charts by querying more than one table at a time","slug":"creating-charts-by-querying-more-than-one-table-at-a-time","posts_count":6,"reply_count":4,"highest_post_number":6,"image_url":null,"created_at":"2015-11-17T11:20:18.442Z","last_posted_at":"2015-11-21T02:12:25.995Z","bumped":true,"bumped_at":"2015-11-21T02:12:25.995Z","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"views":217,"like_count":0,"has_summary":false,"archetype":"regular","last_poster_username":"sameer","category_id":1,"pinned_globally":false,"posters":[{"extras":null,"description":"Original Poster","user_id":51},{"extras":"latest","description":"Most Recent Poster, Frequent Poster","user_id":1}]},{"id":90,"title":"Trying to add RDS postgresql as the database fails silently","fancy_title":"Trying to add RDS postgresql as the database fails silently","slug":"trying-to-add-rds-postgresql-as-the-database-fails-silently","posts_count":4,"reply_count":2,"highest_post_number":4,"image_url":null,"created_at":"2015-11-14T23:45:02.967Z","last_posted_at":"2015-11-21T01:08:45.915Z","bumped":true,"bumped_at":"2015-11-21T01:08:45.915Z","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"views":162,"like_count":0,"has_summary":false,"archetype":"regular","last_poster_username":"sameer","category_id":1,"pinned_globally":false,"posters":[{"extras":null,"description":"Original Poster","user_id":46},{"extras":"latest","description":"Most Recent Poster, Frequent Poster","user_id":1}]},{"id":17,"title":"Deploy to Heroku isn't working","fancy_title":"Deploy to Heroku isn’t working","slug":"deploy-to-heroku-isnt-working","posts_count":9,"reply_count":3,"highest_post_number":9,"image_url":null,"created_at":"2015-10-21T16:42:03.096Z","last_posted_at":"2015-11-20T18:34:14.044Z","bumped":true,"bumped_at":"2015-11-20T18:34:14.044Z","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"views":332,"like_count":2,"has_summary":false,"archetype":"regular","last_poster_username":"agilliland","category_id":1,"pinned_globally":false,"posters":[{"extras":null,"description":"Original Poster","user_id":5},{"extras":null,"description":"Frequent Poster","user_id":3},{"extras":null,"description":"Frequent Poster","user_id":11},{"extras":null,"description":"Frequent Poster","user_id":25},{"extras":"latest","description":"Most Recent Poster","user_id":14}]},{"id":100,"title":"Can I use DATEPART() in SQL queries?","fancy_title":"Can I use DATEPART() in SQL queries?","slug":"can-i-use-datepart-in-sql-queries","posts_count":2,"reply_count":0,"highest_post_number":2,"image_url":null,"created_at":"2015-11-17T23:15:58.033Z","last_posted_at":"2015-11-18T00:19:48.763Z","bumped":true,"bumped_at":"2015-11-18T00:19:48.763Z","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"views":112,"like_count":1,"has_summary":false,"archetype":"regular","last_poster_username":"sameer","category_id":1,"pinned_globally":false,"posters":[{"extras":null,"description":"Original Poster","user_id":53},{"extras":"latest","description":"Most Recent Poster","user_id":1}]},{"id":98,"title":"Feature Request: LDAP Authentication","fancy_title":"Feature Request: LDAP Authentication","slug":"feature-request-ldap-authentication","posts_count":1,"reply_count":0,"highest_post_number":1,"image_url":null,"created_at":"2015-11-17T17:22:44.484Z","last_posted_at":"2015-11-17T17:22:44.577Z","bumped":true,"bumped_at":"2015-11-17T17:22:44.577Z","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"views":97,"like_count":0,"has_summary":false,"archetype":"regular","last_poster_username":"j_at_svg","category_id":1,"pinned_globally":false,"posters":[{"extras":"latest single","description":"Original Poster, Most Recent Poster","user_id":52}]},{"id":87,"title":"Migrating from internal H2 to Postgres","fancy_title":"Migrating from internal H2 to Postgres","slug":"migrating-from-internal-h2-to-postgres","posts_count":2,"reply_count":0,"highest_post_number":2,"image_url":null,"created_at":"2015-11-12T14:36:06.745Z","last_posted_at":"2015-11-12T18:05:10.796Z","bumped":true,"bumped_at":"2015-11-12T18:05:10.796Z","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"views":111,"like_count":0,"has_summary":false,"archetype":"regular","last_poster_username":"sameer","category_id":1,"pinned_globally":false,"posters":[{"extras":null,"description":"Original Poster","user_id":42},{"extras":"latest","description":"Most Recent Poster","user_id":1}]}]}} `) func NewLargePayload() *LargePayload { dsUsers := DSUsers{} dsTopics := DSTopics{} for i := 0; i < 100; i++ { str := "test" + strconv.Itoa(i) dsUsers = append( dsUsers, &DSUser{ Username: str, }, ) dsTopics = append( dsTopics, &DSTopic{ Id: i, Slug: str, }, ) } return &LargePayload{ Users: dsUsers, Topics: &DSTopicsList{ Topics: dsTopics, MoreTopicsUrl: "http://test.com", }, } } golang-github-francoispqt-gojay-1.2.13/benchmarks/benchmarks_large_easyjson.go000066400000000000000000000221471365331665100276500ustar00rootroot00000000000000// Code generated by easyjson for marshaling/unmarshaling. DO NOT EDIT. package benchmarks import ( json "encoding/json" easyjson "github.com/mailru/easyjson" jlexer "github.com/mailru/easyjson/jlexer" jwriter "github.com/mailru/easyjson/jwriter" ) // suppress unused package warning var ( _ *json.RawMessage _ *jlexer.Lexer _ *jwriter.Writer _ easyjson.Marshaler ) func easyjsonCfe4e5f0DecodeGithubComFrancoispqtGojayBenchmarks(in *jlexer.Lexer, out *LargePayload) { isTopLevel := in.IsStart() if in.IsNull() { if isTopLevel { in.Consumed() } in.Skip() return } in.Delim('{') for !in.IsDelim('}') { key := in.UnsafeString() in.WantColon() if in.IsNull() { in.Skip() in.WantComma() continue } switch key { case "Users": if in.IsNull() { in.Skip() out.Users = nil } else { in.Delim('[') if out.Users == nil { if !in.IsDelim(']') { out.Users = make(DSUsers, 0, 8) } else { out.Users = DSUsers{} } } else { out.Users = (out.Users)[:0] } for !in.IsDelim(']') { var v1 *DSUser if in.IsNull() { in.Skip() v1 = nil } else { if v1 == nil { v1 = new(DSUser) } (*v1).UnmarshalEasyJSON(in) } out.Users = append(out.Users, v1) in.WantComma() } in.Delim(']') } case "Topics": if in.IsNull() { in.Skip() out.Topics = nil } else { if out.Topics == nil { out.Topics = new(DSTopicsList) } (*out.Topics).UnmarshalEasyJSON(in) } default: in.SkipRecursive() } in.WantComma() } in.Delim('}') if isTopLevel { in.Consumed() } } func easyjsonCfe4e5f0EncodeGithubComFrancoispqtGojayBenchmarks(out *jwriter.Writer, in LargePayload) { out.RawByte('{') first := true _ = first { const prefix string = ",\"Users\":" if first { first = false out.RawString(prefix[1:]) } else { out.RawString(prefix) } if in.Users == nil && (out.Flags&jwriter.NilSliceAsEmpty) == 0 { out.RawString("null") } else { out.RawByte('[') for v2, v3 := range in.Users { if v2 > 0 { out.RawByte(',') } if v3 == nil { out.RawString("null") } else { (*v3).MarshalEasyJSON(out) } } out.RawByte(']') } } { const prefix string = ",\"Topics\":" if first { first = false out.RawString(prefix[1:]) } else { out.RawString(prefix) } if in.Topics == nil { out.RawString("null") } else { (*in.Topics).MarshalEasyJSON(out) } } out.RawByte('}') } // MarshalJSON supports json.Marshaler interface func (v LargePayload) MarshalJSON() ([]byte, error) { w := jwriter.Writer{} easyjsonCfe4e5f0EncodeGithubComFrancoispqtGojayBenchmarks(&w, v) return w.Buffer.BuildBytes(), w.Error } // MarshalEasyJSON supports easyjson.Marshaler interface func (v LargePayload) MarshalEasyJSON(w *jwriter.Writer) { easyjsonCfe4e5f0EncodeGithubComFrancoispqtGojayBenchmarks(w, v) } // UnmarshalJSON supports json.Unmarshaler interface func (v *LargePayload) UnmarshalJSON(data []byte) error { r := jlexer.Lexer{Data: data} easyjsonCfe4e5f0DecodeGithubComFrancoispqtGojayBenchmarks(&r, v) return r.Error() } // UnmarshalEasyJSON supports easyjson.Unmarshaler interface func (v *LargePayload) UnmarshalEasyJSON(l *jlexer.Lexer) { easyjsonCfe4e5f0DecodeGithubComFrancoispqtGojayBenchmarks(l, v) } func easyjsonCfe4e5f0DecodeGithubComFrancoispqtGojayBenchmarks1(in *jlexer.Lexer, out *DSUser) { isTopLevel := in.IsStart() if in.IsNull() { if isTopLevel { in.Consumed() } in.Skip() return } in.Delim('{') for !in.IsDelim('}') { key := in.UnsafeString() in.WantColon() if in.IsNull() { in.Skip() in.WantComma() continue } switch key { case "Username": out.Username = string(in.String()) default: in.SkipRecursive() } in.WantComma() } in.Delim('}') if isTopLevel { in.Consumed() } } func easyjsonCfe4e5f0EncodeGithubComFrancoispqtGojayBenchmarks1(out *jwriter.Writer, in DSUser) { out.RawByte('{') first := true _ = first { const prefix string = ",\"Username\":" if first { first = false out.RawString(prefix[1:]) } else { out.RawString(prefix) } out.String(string(in.Username)) } out.RawByte('}') } // MarshalJSON supports json.Marshaler interface func (v DSUser) MarshalJSON() ([]byte, error) { w := jwriter.Writer{} easyjsonCfe4e5f0EncodeGithubComFrancoispqtGojayBenchmarks1(&w, v) return w.Buffer.BuildBytes(), w.Error } // MarshalEasyJSON supports easyjson.Marshaler interface func (v DSUser) MarshalEasyJSON(w *jwriter.Writer) { easyjsonCfe4e5f0EncodeGithubComFrancoispqtGojayBenchmarks1(w, v) } // UnmarshalJSON supports json.Unmarshaler interface func (v *DSUser) UnmarshalJSON(data []byte) error { r := jlexer.Lexer{Data: data} easyjsonCfe4e5f0DecodeGithubComFrancoispqtGojayBenchmarks1(&r, v) return r.Error() } // UnmarshalEasyJSON supports easyjson.Unmarshaler interface func (v *DSUser) UnmarshalEasyJSON(l *jlexer.Lexer) { easyjsonCfe4e5f0DecodeGithubComFrancoispqtGojayBenchmarks1(l, v) } func easyjsonCfe4e5f0DecodeGithubComFrancoispqtGojayBenchmarks2(in *jlexer.Lexer, out *DSTopicsList) { isTopLevel := in.IsStart() if in.IsNull() { if isTopLevel { in.Consumed() } in.Skip() return } in.Delim('{') for !in.IsDelim('}') { key := in.UnsafeString() in.WantColon() if in.IsNull() { in.Skip() in.WantComma() continue } switch key { case "Topics": if in.IsNull() { in.Skip() out.Topics = nil } else { in.Delim('[') if out.Topics == nil { if !in.IsDelim(']') { out.Topics = make(DSTopics, 0, 8) } else { out.Topics = DSTopics{} } } else { out.Topics = (out.Topics)[:0] } for !in.IsDelim(']') { var v4 *DSTopic if in.IsNull() { in.Skip() v4 = nil } else { if v4 == nil { v4 = new(DSTopic) } (*v4).UnmarshalEasyJSON(in) } out.Topics = append(out.Topics, v4) in.WantComma() } in.Delim(']') } case "MoreTopicsUrl": out.MoreTopicsUrl = string(in.String()) default: in.SkipRecursive() } in.WantComma() } in.Delim('}') if isTopLevel { in.Consumed() } } func easyjsonCfe4e5f0EncodeGithubComFrancoispqtGojayBenchmarks2(out *jwriter.Writer, in DSTopicsList) { out.RawByte('{') first := true _ = first { const prefix string = ",\"Topics\":" if first { first = false out.RawString(prefix[1:]) } else { out.RawString(prefix) } if in.Topics == nil && (out.Flags&jwriter.NilSliceAsEmpty) == 0 { out.RawString("null") } else { out.RawByte('[') for v5, v6 := range in.Topics { if v5 > 0 { out.RawByte(',') } if v6 == nil { out.RawString("null") } else { (*v6).MarshalEasyJSON(out) } } out.RawByte(']') } } { const prefix string = ",\"MoreTopicsUrl\":" if first { first = false out.RawString(prefix[1:]) } else { out.RawString(prefix) } out.String(string(in.MoreTopicsUrl)) } out.RawByte('}') } // MarshalJSON supports json.Marshaler interface func (v DSTopicsList) MarshalJSON() ([]byte, error) { w := jwriter.Writer{} easyjsonCfe4e5f0EncodeGithubComFrancoispqtGojayBenchmarks2(&w, v) return w.Buffer.BuildBytes(), w.Error } // MarshalEasyJSON supports easyjson.Marshaler interface func (v DSTopicsList) MarshalEasyJSON(w *jwriter.Writer) { easyjsonCfe4e5f0EncodeGithubComFrancoispqtGojayBenchmarks2(w, v) } // UnmarshalJSON supports json.Unmarshaler interface func (v *DSTopicsList) UnmarshalJSON(data []byte) error { r := jlexer.Lexer{Data: data} easyjsonCfe4e5f0DecodeGithubComFrancoispqtGojayBenchmarks2(&r, v) return r.Error() } // UnmarshalEasyJSON supports easyjson.Unmarshaler interface func (v *DSTopicsList) UnmarshalEasyJSON(l *jlexer.Lexer) { easyjsonCfe4e5f0DecodeGithubComFrancoispqtGojayBenchmarks2(l, v) } func easyjsonCfe4e5f0DecodeGithubComFrancoispqtGojayBenchmarks3(in *jlexer.Lexer, out *DSTopic) { isTopLevel := in.IsStart() if in.IsNull() { if isTopLevel { in.Consumed() } in.Skip() return } in.Delim('{') for !in.IsDelim('}') { key := in.UnsafeString() in.WantColon() if in.IsNull() { in.Skip() in.WantComma() continue } switch key { case "Id": out.Id = int(in.Int()) case "Slug": out.Slug = string(in.String()) default: in.SkipRecursive() } in.WantComma() } in.Delim('}') if isTopLevel { in.Consumed() } } func easyjsonCfe4e5f0EncodeGithubComFrancoispqtGojayBenchmarks3(out *jwriter.Writer, in DSTopic) { out.RawByte('{') first := true _ = first { const prefix string = ",\"Id\":" if first { first = false out.RawString(prefix[1:]) } else { out.RawString(prefix) } out.Int(int(in.Id)) } { const prefix string = ",\"Slug\":" if first { first = false out.RawString(prefix[1:]) } else { out.RawString(prefix) } out.String(string(in.Slug)) } out.RawByte('}') } // MarshalEasyJSON supports easyjson.Marshaler interface func (v DSTopic) MarshalEasyJSON(w *jwriter.Writer) { easyjsonCfe4e5f0EncodeGithubComFrancoispqtGojayBenchmarks3(w, v) } // UnmarshalEasyJSON supports easyjson.Unmarshaler interface func (v *DSTopic) UnmarshalEasyJSON(l *jlexer.Lexer) { easyjsonCfe4e5f0DecodeGithubComFrancoispqtGojayBenchmarks3(l, v) } golang-github-francoispqt-gojay-1.2.13/benchmarks/benchmarks_medium.go000066400000000000000000000141021365331665100261130ustar00rootroot00000000000000package benchmarks import "github.com/francoispqt/gojay" // Response from Clearbit API. Size: 2.4kb var MediumFixture = []byte(`{ "person": { "id": "d50887ca-a6ce-4e59-b89f-14f0b5d03b03", "name": { "fullName": "Leonid Bugaev", "givenName": "Leonid", "familyName": "Bugaev" }, "email": "leonsbox@gmail.com", "gender": "male", "location": "Saint Petersburg, Saint Petersburg, RU", "geo": { "city": "Saint Petersburg", "state": "Saint Petersburg", "country": "Russia", "lat": 59.9342802, "lng": 30.3350986 }, "bio": "Senior engineer at Granify.com", "site": "http://flickfaver.com", "avatar": "https://d1ts43dypk8bqh.cloudfront.net/v1/avatars/d50887ca-a6ce-4e59-b89f-14f0b5d03b03", "employment": { "name": "www.latera.ru", "title": "Software Engineer", "domain": "gmail.com" }, "facebook": { "handle": "leonid.bugaev" }, "github": { "handle": "buger", "id": 14009, "avatar": "https://avatars.githubusercontent.com/u/14009?v=3", "company": "Granify", "blog": "http://leonsbox.com", "followers": 95, "following": 10 }, "twitter": { "handle": "flickfaver", "id": 77004410, "bio": null, "followers": 2, "following": 1, "statuses": 5, "favorites": 0, "location": "", "site": "http://flickfaver.com", "avatar": null }, "linkedin": { "handle": "in/leonidbugaev" }, "googleplus": { "handle": null }, "angellist": { "handle": "leonid-bugaev", "id": 61541, "bio": "Senior engineer at Granify.com", "blog": "http://buger.github.com", "site": "http://buger.github.com", "followers": 41, "avatar": "https://d1qb2nb5cznatu.cloudfront.net/users/61541-medium_jpg?1405474390" }, "klout": { "handle": null, "score": null }, "foursquare": { "handle": null }, "aboutme": { "handle": "leonid.bugaev", "bio": null, "avatar": null }, "gravatar": { "handle": "buger", "urls": [ ], "avatar": "http://1.gravatar.com/avatar/f7c8edd577d13b8930d5522f28123510", "avatars": [ { "url": "http://1.gravatar.com/avatar/f7c8edd577d13b8930d5522f28123510", "type": "thumbnail" } ] }, "fuzzy": false }, "company": null }`) type CBAvatar struct { Url string } func (m *CBAvatar) UnmarshalJSONObject(dec *gojay.Decoder, key string) error { switch key { case "avatars": return dec.AddString(&m.Url) } return nil } func (m *CBAvatar) NKeys() int { return 1 } func (m *CBAvatar) MarshalJSONObject(enc *gojay.Encoder) { enc.AddStringKey("url", m.Url) } func (m *CBAvatar) IsNil() bool { return m == nil } type Avatars []*CBAvatar func (t *Avatars) UnmarshalJSONArray(dec *gojay.Decoder) error { avatar := CBAvatar{} *t = append(*t, &avatar) return dec.AddObject(&avatar) } func (m *Avatars) MarshalJSONArray(enc *gojay.Encoder) { for _, e := range *m { enc.AddObject(e) } } func (m *Avatars) IsNil() bool { return m == nil } type CBGravatar struct { Avatars Avatars } func (m *CBGravatar) UnmarshalJSONObject(dec *gojay.Decoder, key string) error { switch key { case "avatars": return dec.AddArray(&m.Avatars) } return nil } func (m *CBGravatar) NKeys() int { return 1 } func (m *CBGravatar) MarshalJSONObject(enc *gojay.Encoder) { enc.AddArrayKey("avatars", &m.Avatars) } func (m *CBGravatar) IsNil() bool { return m == nil } type CBGithub struct { Followers int } func (m *CBGithub) UnmarshalJSONObject(dec *gojay.Decoder, key string) error { switch key { case "followers": return dec.AddInt(&m.Followers) } return nil } func (m *CBGithub) NKeys() int { return 1 } func (m *CBGithub) MarshalJSONObject(enc *gojay.Encoder) { enc.AddIntKey("followers", m.Followers) } func (m *CBGithub) IsNil() bool { return m == nil } type CBName struct { FullName string `json:"fullName"` } func (m *CBName) UnmarshalJSONObject(dec *gojay.Decoder, key string) error { switch key { case "fullName": return dec.AddString(&m.FullName) } return nil } func (m *CBName) NKeys() int { return 1 } func (m *CBName) MarshalJSONObject(enc *gojay.Encoder) { enc.AddStringKey("fullName", m.FullName) } func (m *CBName) IsNil() bool { return m == nil } type CBPerson struct { Name *CBName `json:"name"` Github *CBGithub `json:"github"` Gravatar *CBGravatar } func (m *CBPerson) UnmarshalJSONObject(dec *gojay.Decoder, key string) error { switch key { case "name": m.Name = &CBName{} return dec.AddObject(m.Name) case "github": m.Github = &CBGithub{} return dec.AddObject(m.Github) case "gravatar": m.Gravatar = &CBGravatar{} return dec.AddObject(m.Gravatar) } return nil } func (m *CBPerson) NKeys() int { return 3 } func (m *CBPerson) MarshalJSONObject(enc *gojay.Encoder) { enc.AddObjectKey("name", m.Name) enc.AddObjectKey("github", m.Github) enc.AddObjectKey("gravatar", m.Gravatar) } func (m *CBPerson) IsNil() bool { return m == nil } //easyjson:json type MediumPayload struct { Person *CBPerson `json:"person"` Company string `json:"company"` } func (m *MediumPayload) UnmarshalJSONObject(dec *gojay.Decoder, key string) error { switch key { case "person": m.Person = &CBPerson{} return dec.AddObject(m.Person) case "company": dec.AddString(&m.Company) } return nil } func (m *MediumPayload) NKeys() int { return 2 } func (m *MediumPayload) MarshalJSONObject(enc *gojay.Encoder) { enc.AddObjectKey("person", m.Person) // enc.AddStringKey("company", m.Company) } func (m *MediumPayload) IsNil() bool { return m == nil } func NewMediumPayload() *MediumPayload { return &MediumPayload{ Company: "test", Person: &CBPerson{ Name: &CBName{ FullName: "test", }, Github: &CBGithub{ Followers: 100, }, Gravatar: &CBGravatar{ Avatars: Avatars{ &CBAvatar{ Url: "http://test.com", }, &CBAvatar{ Url: "http://test.com", }, &CBAvatar{ Url: "http://test.com", }, &CBAvatar{ Url: "http://test.com", }, &CBAvatar{ Url: "http://test.com", }, &CBAvatar{ Url: "http://test.com", }, &CBAvatar{ Url: "http://test.com", }, &CBAvatar{ Url: "http://test.com", }, }, }, }, } } golang-github-francoispqt-gojay-1.2.13/benchmarks/benchmarks_medium_easyjson.go000066400000000000000000000303541365331665100300350ustar00rootroot00000000000000// Code generated by easyjson for marshaling/unmarshaling. DO NOT EDIT. package benchmarks import ( json "encoding/json" easyjson "github.com/mailru/easyjson" jlexer "github.com/mailru/easyjson/jlexer" jwriter "github.com/mailru/easyjson/jwriter" ) // suppress unused package warning var ( _ *json.RawMessage _ *jlexer.Lexer _ *jwriter.Writer _ easyjson.Marshaler ) func easyjsonB0500db0DecodeGithubComFrancoispqtGojayBenchmarks(in *jlexer.Lexer, out *MediumPayload) { isTopLevel := in.IsStart() if in.IsNull() { if isTopLevel { in.Consumed() } in.Skip() return } in.Delim('{') for !in.IsDelim('}') { key := in.UnsafeString() in.WantColon() if in.IsNull() { in.Skip() in.WantComma() continue } switch key { case "person": if in.IsNull() { in.Skip() out.Person = nil } else { if out.Person == nil { out.Person = new(CBPerson) } (*out.Person).UnmarshalEasyJSON(in) } case "company": out.Company = string(in.String()) default: in.SkipRecursive() } in.WantComma() } in.Delim('}') if isTopLevel { in.Consumed() } } func easyjsonB0500db0EncodeGithubComFrancoispqtGojayBenchmarks(out *jwriter.Writer, in MediumPayload) { out.RawByte('{') first := true _ = first { const prefix string = ",\"person\":" if first { first = false out.RawString(prefix[1:]) } else { out.RawString(prefix) } if in.Person == nil { out.RawString("null") } else { (*in.Person).MarshalEasyJSON(out) } } { const prefix string = ",\"company\":" if first { first = false out.RawString(prefix[1:]) } else { out.RawString(prefix) } out.String(string(in.Company)) } out.RawByte('}') } // MarshalJSON supports json.Marshaler interface func (v MediumPayload) MarshalJSON() ([]byte, error) { w := jwriter.Writer{} easyjsonB0500db0EncodeGithubComFrancoispqtGojayBenchmarks(&w, v) return w.Buffer.BuildBytes(), w.Error } // MarshalEasyJSON supports easyjson.Marshaler interface func (v MediumPayload) MarshalEasyJSON(w *jwriter.Writer) { easyjsonB0500db0EncodeGithubComFrancoispqtGojayBenchmarks(w, v) } // UnmarshalJSON supports json.Unmarshaler interface func (v *MediumPayload) UnmarshalJSON(data []byte) error { r := jlexer.Lexer{Data: data} easyjsonB0500db0DecodeGithubComFrancoispqtGojayBenchmarks(&r, v) return r.Error() } // UnmarshalEasyJSON supports easyjson.Unmarshaler interface func (v *MediumPayload) UnmarshalEasyJSON(l *jlexer.Lexer) { easyjsonB0500db0DecodeGithubComFrancoispqtGojayBenchmarks(l, v) } func easyjsonB0500db0DecodeGithubComFrancoispqtGojayBenchmarks1(in *jlexer.Lexer, out *CBPerson) { isTopLevel := in.IsStart() if in.IsNull() { if isTopLevel { in.Consumed() } in.Skip() return } in.Delim('{') for !in.IsDelim('}') { key := in.UnsafeString() in.WantColon() if in.IsNull() { in.Skip() in.WantComma() continue } switch key { case "name": if in.IsNull() { in.Skip() out.Name = nil } else { if out.Name == nil { out.Name = new(CBName) } (*out.Name).UnmarshalEasyJSON(in) } case "github": if in.IsNull() { in.Skip() out.Github = nil } else { if out.Github == nil { out.Github = new(CBGithub) } (*out.Github).UnmarshalEasyJSON(in) } case "Gravatar": if in.IsNull() { in.Skip() out.Gravatar = nil } else { if out.Gravatar == nil { out.Gravatar = new(CBGravatar) } (*out.Gravatar).UnmarshalEasyJSON(in) } default: in.SkipRecursive() } in.WantComma() } in.Delim('}') if isTopLevel { in.Consumed() } } func easyjsonB0500db0EncodeGithubComFrancoispqtGojayBenchmarks1(out *jwriter.Writer, in CBPerson) { out.RawByte('{') first := true _ = first { const prefix string = ",\"name\":" if first { first = false out.RawString(prefix[1:]) } else { out.RawString(prefix) } if in.Name == nil { out.RawString("null") } else { (*in.Name).MarshalEasyJSON(out) } } { const prefix string = ",\"github\":" if first { first = false out.RawString(prefix[1:]) } else { out.RawString(prefix) } if in.Github == nil { out.RawString("null") } else { (*in.Github).MarshalEasyJSON(out) } } { const prefix string = ",\"Gravatar\":" if first { first = false out.RawString(prefix[1:]) } else { out.RawString(prefix) } if in.Gravatar == nil { out.RawString("null") } else { (*in.Gravatar).MarshalEasyJSON(out) } } out.RawByte('}') } // MarshalJSON supports json.Marshaler interface func (v CBPerson) MarshalJSON() ([]byte, error) { w := jwriter.Writer{} easyjsonB0500db0EncodeGithubComFrancoispqtGojayBenchmarks1(&w, v) return w.Buffer.BuildBytes(), w.Error } // MarshalEasyJSON supports easyjson.Marshaler interface func (v CBPerson) MarshalEasyJSON(w *jwriter.Writer) { easyjsonB0500db0EncodeGithubComFrancoispqtGojayBenchmarks1(w, v) } // UnmarshalJSON supports json.Unmarshaler interface func (v *CBPerson) UnmarshalJSON(data []byte) error { r := jlexer.Lexer{Data: data} easyjsonB0500db0DecodeGithubComFrancoispqtGojayBenchmarks1(&r, v) return r.Error() } // UnmarshalEasyJSON supports easyjson.Unmarshaler interface func (v *CBPerson) UnmarshalEasyJSON(l *jlexer.Lexer) { easyjsonB0500db0DecodeGithubComFrancoispqtGojayBenchmarks1(l, v) } func easyjsonB0500db0DecodeGithubComFrancoispqtGojayBenchmarks2(in *jlexer.Lexer, out *CBName) { isTopLevel := in.IsStart() if in.IsNull() { if isTopLevel { in.Consumed() } in.Skip() return } in.Delim('{') for !in.IsDelim('}') { key := in.UnsafeString() in.WantColon() if in.IsNull() { in.Skip() in.WantComma() continue } switch key { case "fullName": out.FullName = string(in.String()) default: in.SkipRecursive() } in.WantComma() } in.Delim('}') if isTopLevel { in.Consumed() } } func easyjsonB0500db0EncodeGithubComFrancoispqtGojayBenchmarks2(out *jwriter.Writer, in CBName) { out.RawByte('{') first := true _ = first { const prefix string = ",\"fullName\":" if first { first = false out.RawString(prefix[1:]) } else { out.RawString(prefix) } out.String(string(in.FullName)) } out.RawByte('}') } // MarshalJSON supports json.Marshaler interface func (v CBName) MarshalJSON() ([]byte, error) { w := jwriter.Writer{} easyjsonB0500db0EncodeGithubComFrancoispqtGojayBenchmarks2(&w, v) return w.Buffer.BuildBytes(), w.Error } // MarshalEasyJSON supports easyjson.Marshaler interface func (v CBName) MarshalEasyJSON(w *jwriter.Writer) { easyjsonB0500db0EncodeGithubComFrancoispqtGojayBenchmarks2(w, v) } // UnmarshalJSON supports json.Unmarshaler interface func (v *CBName) UnmarshalJSON(data []byte) error { r := jlexer.Lexer{Data: data} easyjsonB0500db0DecodeGithubComFrancoispqtGojayBenchmarks2(&r, v) return r.Error() } // UnmarshalEasyJSON supports easyjson.Unmarshaler interface func (v *CBName) UnmarshalEasyJSON(l *jlexer.Lexer) { easyjsonB0500db0DecodeGithubComFrancoispqtGojayBenchmarks2(l, v) } func easyjsonB0500db0DecodeGithubComFrancoispqtGojayBenchmarks3(in *jlexer.Lexer, out *CBGravatar) { isTopLevel := in.IsStart() if in.IsNull() { if isTopLevel { in.Consumed() } in.Skip() return } in.Delim('{') for !in.IsDelim('}') { key := in.UnsafeString() in.WantColon() if in.IsNull() { in.Skip() in.WantComma() continue } switch key { case "Avatars": if in.IsNull() { in.Skip() out.Avatars = nil } else { in.Delim('[') if out.Avatars == nil { if !in.IsDelim(']') { out.Avatars = make(Avatars, 0, 8) } else { out.Avatars = Avatars{} } } else { out.Avatars = (out.Avatars)[:0] } for !in.IsDelim(']') { var v1 *CBAvatar if in.IsNull() { in.Skip() v1 = nil } else { if v1 == nil { v1 = new(CBAvatar) } (*v1).UnmarshalEasyJSON(in) } out.Avatars = append(out.Avatars, v1) in.WantComma() } in.Delim(']') } default: in.SkipRecursive() } in.WantComma() } in.Delim('}') if isTopLevel { in.Consumed() } } func easyjsonB0500db0EncodeGithubComFrancoispqtGojayBenchmarks3(out *jwriter.Writer, in CBGravatar) { out.RawByte('{') first := true _ = first { const prefix string = ",\"Avatars\":" if first { first = false out.RawString(prefix[1:]) } else { out.RawString(prefix) } if in.Avatars == nil && (out.Flags&jwriter.NilSliceAsEmpty) == 0 { out.RawString("null") } else { out.RawByte('[') for v2, v3 := range in.Avatars { if v2 > 0 { out.RawByte(',') } if v3 == nil { out.RawString("null") } else { (*v3).MarshalEasyJSON(out) } } out.RawByte(']') } } out.RawByte('}') } // MarshalJSON supports json.Marshaler interface func (v CBGravatar) MarshalJSON() ([]byte, error) { w := jwriter.Writer{} easyjsonB0500db0EncodeGithubComFrancoispqtGojayBenchmarks3(&w, v) return w.Buffer.BuildBytes(), w.Error } // MarshalEasyJSON supports easyjson.Marshaler interface func (v CBGravatar) MarshalEasyJSON(w *jwriter.Writer) { easyjsonB0500db0EncodeGithubComFrancoispqtGojayBenchmarks3(w, v) } // UnmarshalJSON supports json.Unmarshaler interface func (v *CBGravatar) UnmarshalJSON(data []byte) error { r := jlexer.Lexer{Data: data} easyjsonB0500db0DecodeGithubComFrancoispqtGojayBenchmarks3(&r, v) return r.Error() } // UnmarshalEasyJSON supports easyjson.Unmarshaler interface func (v *CBGravatar) UnmarshalEasyJSON(l *jlexer.Lexer) { easyjsonB0500db0DecodeGithubComFrancoispqtGojayBenchmarks3(l, v) } func easyjsonB0500db0DecodeGithubComFrancoispqtGojayBenchmarks4(in *jlexer.Lexer, out *CBGithub) { isTopLevel := in.IsStart() if in.IsNull() { if isTopLevel { in.Consumed() } in.Skip() return } in.Delim('{') for !in.IsDelim('}') { key := in.UnsafeString() in.WantColon() if in.IsNull() { in.Skip() in.WantComma() continue } switch key { case "Followers": out.Followers = int(in.Int()) default: in.SkipRecursive() } in.WantComma() } in.Delim('}') if isTopLevel { in.Consumed() } } func easyjsonB0500db0EncodeGithubComFrancoispqtGojayBenchmarks4(out *jwriter.Writer, in CBGithub) { out.RawByte('{') first := true _ = first { const prefix string = ",\"Followers\":" if first { first = false out.RawString(prefix[1:]) } else { out.RawString(prefix) } out.Int(int(in.Followers)) } out.RawByte('}') } // MarshalJSON supports json.Marshaler interface func (v CBGithub) MarshalJSON() ([]byte, error) { w := jwriter.Writer{} easyjsonB0500db0EncodeGithubComFrancoispqtGojayBenchmarks4(&w, v) return w.Buffer.BuildBytes(), w.Error } // MarshalEasyJSON supports easyjson.Marshaler interface func (v CBGithub) MarshalEasyJSON(w *jwriter.Writer) { easyjsonB0500db0EncodeGithubComFrancoispqtGojayBenchmarks4(w, v) } // UnmarshalJSON supports json.Unmarshaler interface func (v *CBGithub) UnmarshalJSON(data []byte) error { r := jlexer.Lexer{Data: data} easyjsonB0500db0DecodeGithubComFrancoispqtGojayBenchmarks4(&r, v) return r.Error() } // UnmarshalEasyJSON supports easyjson.Unmarshaler interface func (v *CBGithub) UnmarshalEasyJSON(l *jlexer.Lexer) { easyjsonB0500db0DecodeGithubComFrancoispqtGojayBenchmarks4(l, v) } func easyjsonB0500db0DecodeGithubComFrancoispqtGojayBenchmarks5(in *jlexer.Lexer, out *CBAvatar) { isTopLevel := in.IsStart() if in.IsNull() { if isTopLevel { in.Consumed() } in.Skip() return } in.Delim('{') for !in.IsDelim('}') { key := in.UnsafeString() in.WantColon() if in.IsNull() { in.Skip() in.WantComma() continue } switch key { case "Url": out.Url = string(in.String()) default: in.SkipRecursive() } in.WantComma() } in.Delim('}') if isTopLevel { in.Consumed() } } func easyjsonB0500db0EncodeGithubComFrancoispqtGojayBenchmarks5(out *jwriter.Writer, in CBAvatar) { out.RawByte('{') first := true _ = first { const prefix string = ",\"Url\":" if first { first = false out.RawString(prefix[1:]) } else { out.RawString(prefix) } out.String(string(in.Url)) } out.RawByte('}') } // MarshalEasyJSON supports easyjson.Marshaler interface func (v CBAvatar) MarshalEasyJSON(w *jwriter.Writer) { easyjsonB0500db0EncodeGithubComFrancoispqtGojayBenchmarks5(w, v) } // UnmarshalEasyJSON supports easyjson.Unmarshaler interface func (v *CBAvatar) UnmarshalEasyJSON(l *jlexer.Lexer) { easyjsonB0500db0DecodeGithubComFrancoispqtGojayBenchmarks5(l, v) } golang-github-francoispqt-gojay-1.2.13/benchmarks/benchmarks_small.go000066400000000000000000000027701365331665100257530ustar00rootroot00000000000000package benchmarks import "github.com/francoispqt/gojay" var SmallFixture = []byte(`{"st": 1,"sid": 486,"tt": "active","gr": 0,"uuid": "de305d54-75b4-431b-adb2-eb6b9e546014","ip": "127.0.0.1","ua": "user_agent","tz": -6,"v": 1}`) //easyjson:json type SmallPayload struct { St int Sid int Tt string Gr int Uuid string Ip string Ua string Tz int V int } func (t *SmallPayload) MarshalJSONObject(enc *gojay.Encoder) { enc.AddIntKey("st", t.St) enc.AddIntKey("sid", t.Sid) enc.AddStringKey("tt", t.Tt) enc.AddIntKey("gr", t.Gr) enc.AddStringKey("uuid", t.Uuid) enc.AddStringKey("ip", t.Ip) enc.AddStringKey("ua", t.Ua) enc.AddIntKey("tz", t.Tz) enc.AddIntKey("v", t.V) } func (t *SmallPayload) IsNil() bool { return t == nil } func (t *SmallPayload) UnmarshalJSONObject(dec *gojay.Decoder, key string) error { switch key { case "st": return dec.AddInt(&t.St) case "sid": return dec.AddInt(&t.Sid) case "gr": return dec.AddInt(&t.Gr) case "tz": return dec.AddInt(&t.Tz) case "v": return dec.AddInt(&t.V) case "tt": return dec.AddString(&t.Tt) case "uuid": return dec.AddString(&t.Uuid) case "ip": return dec.AddString(&t.Ip) case "ua": return dec.AddString(&t.Ua) } return nil } func (t *SmallPayload) NKeys() int { return 9 } func NewSmallPayload() *SmallPayload { return &SmallPayload{ St: 1, Sid: 2, Tt: "TestString", Gr: 4, Uuid: "8f9a65eb-4807-4d57-b6e0-bda5d62f1429", Ip: "127.0.0.1", Ua: "Mozilla", Tz: 8, V: 6, } } golang-github-francoispqt-gojay-1.2.13/benchmarks/benchmarks_small_easyjson.go000066400000000000000000000072231365331665100276640ustar00rootroot00000000000000// Code generated by easyjson for marshaling/unmarshaling. DO NOT EDIT. package benchmarks import ( json "encoding/json" easyjson "github.com/mailru/easyjson" jlexer "github.com/mailru/easyjson/jlexer" jwriter "github.com/mailru/easyjson/jwriter" ) // suppress unused package warning var ( _ *json.RawMessage _ *jlexer.Lexer _ *jwriter.Writer _ easyjson.Marshaler ) func easyjson890029d8DecodeGithubComFrancoispqtGojayBenchmarks(in *jlexer.Lexer, out *SmallPayload) { isTopLevel := in.IsStart() if in.IsNull() { if isTopLevel { in.Consumed() } in.Skip() return } in.Delim('{') for !in.IsDelim('}') { key := in.UnsafeString() in.WantColon() if in.IsNull() { in.Skip() in.WantComma() continue } switch key { case "St": out.St = int(in.Int()) case "Sid": out.Sid = int(in.Int()) case "Tt": out.Tt = string(in.String()) case "Gr": out.Gr = int(in.Int()) case "Uuid": out.Uuid = string(in.String()) case "Ip": out.Ip = string(in.String()) case "Ua": out.Ua = string(in.String()) case "Tz": out.Tz = int(in.Int()) case "V": out.V = int(in.Int()) default: in.SkipRecursive() } in.WantComma() } in.Delim('}') if isTopLevel { in.Consumed() } } func easyjson890029d8EncodeGithubComFrancoispqtGojayBenchmarks(out *jwriter.Writer, in SmallPayload) { out.RawByte('{') first := true _ = first { const prefix string = ",\"St\":" if first { first = false out.RawString(prefix[1:]) } else { out.RawString(prefix) } out.Int(int(in.St)) } { const prefix string = ",\"Sid\":" if first { first = false out.RawString(prefix[1:]) } else { out.RawString(prefix) } out.Int(int(in.Sid)) } { const prefix string = ",\"Tt\":" if first { first = false out.RawString(prefix[1:]) } else { out.RawString(prefix) } out.String(string(in.Tt)) } { const prefix string = ",\"Gr\":" if first { first = false out.RawString(prefix[1:]) } else { out.RawString(prefix) } out.Int(int(in.Gr)) } { const prefix string = ",\"Uuid\":" if first { first = false out.RawString(prefix[1:]) } else { out.RawString(prefix) } out.String(string(in.Uuid)) } { const prefix string = ",\"Ip\":" if first { first = false out.RawString(prefix[1:]) } else { out.RawString(prefix) } out.String(string(in.Ip)) } { const prefix string = ",\"Ua\":" if first { first = false out.RawString(prefix[1:]) } else { out.RawString(prefix) } out.String(string(in.Ua)) } { const prefix string = ",\"Tz\":" if first { first = false out.RawString(prefix[1:]) } else { out.RawString(prefix) } out.Int(int(in.Tz)) } { const prefix string = ",\"V\":" if first { first = false out.RawString(prefix[1:]) } else { out.RawString(prefix) } out.Int(int(in.V)) } out.RawByte('}') } // MarshalJSON supports json.Marshaler interface func (v SmallPayload) MarshalJSON() ([]byte, error) { w := jwriter.Writer{} easyjson890029d8EncodeGithubComFrancoispqtGojayBenchmarks(&w, v) return w.Buffer.BuildBytes(), w.Error } // MarshalEasyJSON supports easyjson.Marshaler interface func (v SmallPayload) MarshalEasyJSON(w *jwriter.Writer) { easyjson890029d8EncodeGithubComFrancoispqtGojayBenchmarks(w, v) } // UnmarshalJSON supports json.Unmarshaler interface func (v *SmallPayload) UnmarshalJSON(data []byte) error { r := jlexer.Lexer{Data: data} easyjson890029d8DecodeGithubComFrancoispqtGojayBenchmarks(&r, v) return r.Error() } // UnmarshalEasyJSON supports easyjson.Unmarshaler interface func (v *SmallPayload) UnmarshalEasyJSON(l *jlexer.Lexer) { easyjson890029d8DecodeGithubComFrancoispqtGojayBenchmarks(l, v) } golang-github-francoispqt-gojay-1.2.13/benchmarks/decoder/000077500000000000000000000000001365331665100235165ustar00rootroot00000000000000golang-github-francoispqt-gojay-1.2.13/benchmarks/decoder/.gitignore000066400000000000000000000000101365331665100254750ustar00rootroot00000000000000vendor/*golang-github-francoispqt-gojay-1.2.13/benchmarks/decoder/Makefile000066400000000000000000000021141365331665100251540ustar00rootroot00000000000000.PHONY: test test: go test -run=^Test -v .PHONY: bench bench: go test -benchmem -run=^$ github.com/francoispqt/gojay/benchmarks/decoder -bench ^Benchmark .PHONY: benchtrace benchtrace: go test -c & GODEBUG=allocfreetrace=1 ./decoder.test -test.run=none -test.bench=^Benchmark -test.benchtime=10ms 2>trace.log .PHONY: benchcpu benchcpu: go test -benchmem -run=^$ github.com/francoispqt/gojay/benchmarks/decoder -bench ^Benchmark -cpuprofile cpu.out .PHONY: testtrace testtrace: go test -benchmem -run=^$ github.com/francoispqt/gojay/benchmarks/decoder -bench ^Benchmark -trace trace.out .PHONY: benchgojay benchgojay: go test -benchmem -run=^BenchmarkGoJay -bench=^BenchmarkGoJay -benchtime=30ms .PHONY: benchgojaycpu benchgojaycpu: go test -benchmem -run=^BenchmarkGoJay -bench=^BenchmarkGoJay -benchtime=30ms -cpuprofile cpu.out .PHONY: benchjsoniter benchjsoniter: go test -benchmem -run=^BenchmarkJsonIter -bench=^BenchmarkJsonIter -benchtime=30ms .PHONY: benchjsonparser benchjsonparser: go test -benchmem -run=^BenchmarkJsonParser -bench=^BenchmarkJsonParser -benchtime=30msgolang-github-francoispqt-gojay-1.2.13/benchmarks/decoder/decoder.go000066400000000000000000000000231365331665100254450ustar00rootroot00000000000000package benchmarks golang-github-francoispqt-gojay-1.2.13/benchmarks/decoder/decoder_bench_float_test.go000066400000000000000000000010771365331665100310420ustar00rootroot00000000000000package benchmarks import ( "encoding/json" "testing" "github.com/francoispqt/gojay" ) var bigf = []byte(`0.00058273999999999999`) // BenchmarkBigFloatEncodingJSON decodes a big float with the standard package func BenchmarkBigFloatEncodingJSON(b *testing.B) { b.ReportAllocs() for n := 0; n < b.N; n++ { var f float64 var _ = json.Unmarshal(bigf, &f) } } // BenchmarkBigFloatGojay decodes a big float with gojay func BenchmarkBigFloatGojay(b *testing.B) { b.ReportAllocs() for n := 0; n < b.N; n++ { var f float64 var _ = gojay.Unmarshal(bigf, &f) } } golang-github-francoispqt-gojay-1.2.13/benchmarks/decoder/decoder_bench_large_test.go000066400000000000000000000030231365331665100310200ustar00rootroot00000000000000package benchmarks import ( "testing" "github.com/buger/jsonparser" "github.com/francoispqt/gojay" "github.com/francoispqt/gojay/benchmarks" jsoniter "github.com/json-iterator/go" "github.com/mailru/easyjson" ) func BenchmarkJsonParserDecodeObjLarge(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { jsonparser.ArrayEach(benchmarks.LargeFixture, func(value []byte, dataType jsonparser.ValueType, offset int, err error) { jsonparser.Get(value, "username") nothing() }, "users") jsonparser.ArrayEach(benchmarks.LargeFixture, func(value []byte, dataType jsonparser.ValueType, offset int, err error) { jsonparser.GetInt(value, "id") jsonparser.Get(value, "slug") nothing() }, "topics", "topics") } } func BenchmarkJsonIterDecodeObjLarge(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { result := benchmarks.LargePayload{} jsoniter.Unmarshal(benchmarks.LargeFixture, &result) } } func BenchmarkEasyJsonDecodeObjLarge(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { result := benchmarks.LargePayload{} easyjson.Unmarshal(benchmarks.LargeFixture, &result) } } func BenchmarkGoJayDecodeObjLarge(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { result := benchmarks.LargePayload{} gojay.UnmarshalJSONObject(benchmarks.LargeFixture, &result) } } func BenchmarkGoJayUnsafeDecodeObjLarge(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { result := benchmarks.LargePayload{} gojay.Unsafe.UnmarshalJSONObject(benchmarks.LargeFixture, &result) } } golang-github-francoispqt-gojay-1.2.13/benchmarks/decoder/decoder_bench_medium_test.go000066400000000000000000000035371365331665100312200ustar00rootroot00000000000000package benchmarks import ( "encoding/json" "testing" "github.com/buger/jsonparser" "github.com/francoispqt/gojay" "github.com/francoispqt/gojay/benchmarks" jsoniter "github.com/json-iterator/go" "github.com/mailru/easyjson" ) func BenchmarkJsonIterDecodeObjMedium(b *testing.B) { b.ReportAllocs() for n := 0; n < b.N; n++ { result := benchmarks.MediumPayload{} jsoniter.Unmarshal(benchmarks.MediumFixture, &result) } } /* github.com/buger/jsonparser */ func BenchmarkJSONParserDecodeObjMedium(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { jsonparser.Get(benchmarks.MediumFixture, "person", "name", "fullName") jsonparser.GetInt(benchmarks.MediumFixture, "person", "github", "followers") jsonparser.Get(benchmarks.MediumFixture, "company") jsonparser.ArrayEach(benchmarks.MediumFixture, func(value []byte, dataType jsonparser.ValueType, offset int, err error) { jsonparser.Get(value, "url") nothing() }, "person", "gravatar", "avatars") } } func BenchmarkEncodingJsonStructMedium(b *testing.B) { for i := 0; i < b.N; i++ { var data = benchmarks.MediumPayload{} json.Unmarshal(benchmarks.MediumFixture, &data) } } func BenchmarkEasyJsonDecodeObjMedium(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { result := benchmarks.MediumPayload{} easyjson.Unmarshal(benchmarks.MediumFixture, &result) } } func BenchmarkGoJayDecodeObjMedium(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { result := benchmarks.MediumPayload{} err := gojay.UnmarshalJSONObject(benchmarks.MediumFixture, &result) if err != nil { b.Error(err) } } } func BenchmarkGoJayUnsafeDecodeObjMedium(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { result := benchmarks.MediumPayload{} err := gojay.Unsafe.UnmarshalJSONObject(benchmarks.MediumFixture, &result) if err != nil { b.Error(err) } } } golang-github-francoispqt-gojay-1.2.13/benchmarks/decoder/decoder_bench_small_test.go000066400000000000000000000033221365331665100310400ustar00rootroot00000000000000package benchmarks import ( "encoding/json" _ "fmt" "testing" "github.com/buger/jsonparser" "github.com/francoispqt/gojay" "github.com/francoispqt/gojay/benchmarks" jsoniter "github.com/json-iterator/go" "github.com/mailru/easyjson" ) func BenchmarkJSONDecodeObjSmall(b *testing.B) { b.ReportAllocs() for n := 0; n < b.N; n++ { result := benchmarks.SmallPayload{} json.Unmarshal(benchmarks.SmallFixture, &result) } } func nothing(_ ...interface{}) {} func BenchmarkJSONParserSmall(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { jsonparser.GetInt(benchmarks.SmallFixture, "tz") jsonparser.GetInt(benchmarks.SmallFixture, "v") jsonparser.GetInt(benchmarks.SmallFixture, "sid") jsonparser.GetInt(benchmarks.SmallFixture, "st") jsonparser.GetInt(benchmarks.SmallFixture, "gr") jsonparser.Get(benchmarks.SmallFixture, "uuid") jsonparser.Get(benchmarks.SmallFixture, "ua") nothing() } } func BenchmarkJsonIterDecodeObjSmall(b *testing.B) { b.ReportAllocs() for n := 0; n < b.N; n++ { result := benchmarks.SmallPayload{} jsoniter.Unmarshal(benchmarks.SmallFixture, &result) } } func BenchmarkEasyJsonDecodeObjSmall(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { result := benchmarks.SmallPayload{} easyjson.Unmarshal(benchmarks.SmallFixture, &result) } } func BenchmarkGoJayDecodeObjSmall(b *testing.B) { b.ReportAllocs() for n := 0; n < b.N; n++ { result := benchmarks.SmallPayload{} gojay.UnmarshalJSONObject(benchmarks.SmallFixture, &result) } } func BenchmarkGoJayUnsafeDecodeObjSmall(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { result := benchmarks.SmallPayload{} gojay.Unsafe.UnmarshalJSONObject(benchmarks.SmallFixture, &result) } } golang-github-francoispqt-gojay-1.2.13/benchmarks/decoder/decoder_large_test.go000066400000000000000000000012571365331665100276700ustar00rootroot00000000000000package benchmarks import ( "testing" "github.com/francoispqt/gojay" "github.com/francoispqt/gojay/benchmarks" "github.com/stretchr/testify/assert" ) func TestGoJayDecodeObjLarge(t *testing.T) { result := benchmarks.LargePayload{} err := gojay.UnmarshalJSONObject(benchmarks.LargeFixture, &result) assert.Nil(t, err, "err should be nil") assert.Len(t, result.Users, 32, "Len of users should be 32") for _, u := range result.Users { assert.True(t, len(u.Username) > 0, "User should have username") } assert.Len(t, result.Topics.Topics, 30, "Len of topics should be 30") for _, top := range result.Topics.Topics { assert.True(t, top.Id > 0, "Topic should have Id") } } golang-github-francoispqt-gojay-1.2.13/benchmarks/decoder/decoder_medium_test.go000066400000000000000000000012301365331665100300450ustar00rootroot00000000000000package benchmarks import ( "testing" "github.com/francoispqt/gojay" "github.com/francoispqt/gojay/benchmarks" "github.com/stretchr/testify/assert" ) func TestGoJayDecodeObjMedium(t *testing.T) { result := benchmarks.MediumPayload{} err := gojay.Unmarshal(benchmarks.MediumFixture, &result) assert.Nil(t, err, "err should be nil") assert.Equal(t, "Leonid Bugaev", result.Person.Name.FullName, "result.Person.Name.FullName should be Leonid Bugaev") assert.Equal(t, 95, result.Person.Github.Followers, "result.Person.Github.Followers should be 95") assert.Len(t, result.Person.Gravatar.Avatars, 1, "result.Person.Gravatar.Avatars should have 1 item") } golang-github-francoispqt-gojay-1.2.13/benchmarks/decoder/decoder_small_test.go000066400000000000000000000017421365331665100277050ustar00rootroot00000000000000package benchmarks import ( "testing" "github.com/francoispqt/gojay" "github.com/francoispqt/gojay/benchmarks" "github.com/stretchr/testify/assert" ) func TestGoJayDecodeObjSmall(t *testing.T) { result := benchmarks.SmallPayload{} err := gojay.Unmarshal(benchmarks.SmallFixture, &result) assert.Nil(t, err, "err should be nil") assert.Equal(t, result.St, 1, "result.St should be 1") assert.Equal(t, result.Sid, 486, "result.Sid should be 486") assert.Equal(t, result.Tt, "active", "result.Sid should be 'active'") assert.Equal(t, result.Gr, 0, "result.Gr should be 0") assert.Equal( t, result.Uuid, "de305d54-75b4-431b-adb2-eb6b9e546014", "result.Gr should be 'de305d54-75b4-431b-adb2-eb6b9e546014'", ) assert.Equal(t, result.Ip, "127.0.0.1", "result.Ip should be '127.0.0.1'") assert.Equal(t, result.Ua, "user_agent", "result.Ua should be 'user_agent'") assert.Equal(t, result.Tz, -6, "result.Tz should be 6") assert.Equal(t, result.V, 1, "result.V should be 1") } golang-github-francoispqt-gojay-1.2.13/benchmarks/encoder/000077500000000000000000000000001365331665100235305ustar00rootroot00000000000000golang-github-francoispqt-gojay-1.2.13/benchmarks/encoder/.gitignore000066400000000000000000000000101365331665100255070ustar00rootroot00000000000000vendor/*golang-github-francoispqt-gojay-1.2.13/benchmarks/encoder/Makefile000066400000000000000000000020411365331665100251650ustar00rootroot00000000000000.PHONY: bench bench: go test -benchmem -run=^$ github.com/francoispqt/gojay/benchmarks/encoder -bench ^Benchmark .PHONY: benchtrace benchtrace: go test -c & GODEBUG=allocfreetrace=1 ./encoder.test -test.run=none -test.bench=^Benchmark -test.benchtime=10ms 2>trace.log .PHONY: benchcpu benchcpu: go test -benchmem -run=^$ github.com/francoispqt/gojay/benchmarks/encoder -bench ^Benchmark -cpuprofile cpu.out .PHONY: testtrace testtrace: go test -benchmem -run=^$ github.com/francoispqt/gojay/benchmarks/encoder -bench ^Benchmark -trace trace.out .PHONY: benchgojay benchgojay: go test -benchmem -run=^BenchmarkGoJay -bench=^BenchmarkGoJay -benchtime=30ms .PHONY: benchgojaycpu benchgojaycpu: go test -benchmem -run=^BenchmarkGoJay -bench=^BenchmarkGoJay -benchtime=30ms -cpuprofile cpu.out .PHONY: benchjsoniter benchjsoniter: go test -benchmem -run=^BenchmarkJsonIter -bench=^BenchmarkJsonIter -benchtime=30ms .PHONY: benchjsonparser benchjsonparser: go test -benchmem -run=^BenchmarkJsonParser -bench=^BenchmarkJsonParser -benchtime=30msgolang-github-francoispqt-gojay-1.2.13/benchmarks/encoder/encoder_bench_large_test.go000066400000000000000000000023731365331665100310530ustar00rootroot00000000000000package benchmarks import ( "encoding/json" "log" "testing" "github.com/francoispqt/gojay" "github.com/francoispqt/gojay/benchmarks" jsoniter "github.com/json-iterator/go" "github.com/mailru/easyjson" ) func BenchmarkEncodingJsonEncodeLargeStruct(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { if _, err := json.Marshal(benchmarks.NewLargePayload()); err != nil { b.Fatal(err) } } } func BenchmarkJsonIterEncodeLargeStruct(b *testing.B) { var json = jsoniter.ConfigCompatibleWithStandardLibrary b.ReportAllocs() for i := 0; i < b.N; i++ { if _, err := json.Marshal(benchmarks.NewLargePayload()); err != nil { b.Fatal(err) } } } func BenchmarkEasyJsonEncodeObjLarge(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { if _, err := easyjson.Marshal(benchmarks.NewLargePayload()); err != nil { b.Fatal(err) } } } func BenchmarkGoJayEncodeLargeStruct(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { if _, err := gojay.MarshalJSONObject(benchmarks.NewLargePayload()); err != nil { b.Fatal(err) } } } func TestGoJayEncodeLargeStruct(t *testing.T) { if output, err := gojay.MarshalJSONObject(benchmarks.NewLargePayload()); err != nil { t.Fatal(err) } else { log.Print(string(output)) } } golang-github-francoispqt-gojay-1.2.13/benchmarks/encoder/encoder_bench_medium_test.go000066400000000000000000000024061365331665100312360ustar00rootroot00000000000000package benchmarks import ( "encoding/json" "log" "testing" "github.com/francoispqt/gojay" "github.com/francoispqt/gojay/benchmarks" jsoniter "github.com/json-iterator/go" "github.com/mailru/easyjson" ) func BenchmarkEncodingJsonEncodeMediumStruct(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { if _, err := json.Marshal(benchmarks.NewMediumPayload()); err != nil { b.Fatal(err) } } } func BenchmarkJsonIterEncodeMediumStruct(b *testing.B) { var json = jsoniter.ConfigCompatibleWithStandardLibrary b.ReportAllocs() for i := 0; i < b.N; i++ { if _, err := json.Marshal(benchmarks.NewMediumPayload()); err != nil { b.Fatal(err) } } } func BenchmarkEasyJsonEncodeObjMedium(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { if _, err := easyjson.Marshal(benchmarks.NewMediumPayload()); err != nil { b.Fatal(err) } } } func BenchmarkGoJayEncodeMediumStruct(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { if _, err := gojay.MarshalJSONObject(benchmarks.NewMediumPayload()); err != nil { b.Fatal(err) } } } func TestGoJayEncodeMediumStruct(t *testing.T) { if output, err := gojay.MarshalJSONObject(benchmarks.NewMediumPayload()); err != nil { t.Fatal(err) } else { log.Print(string(output)) } } golang-github-francoispqt-gojay-1.2.13/benchmarks/encoder/encoder_bench_small_test.go000066400000000000000000000033501365331665100310650ustar00rootroot00000000000000package benchmarks import ( "encoding/json" "log" "testing" "github.com/francoispqt/gojay" "github.com/francoispqt/gojay/benchmarks" jsoniter "github.com/json-iterator/go" "github.com/mailru/easyjson" ) func BenchmarkEncodingJsonEncodeSmallStruct(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { if _, err := json.Marshal(benchmarks.NewSmallPayload()); err != nil { b.Fatal(err) } } } func BenchmarkEasyJsonEncodeObjSmall(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { if _, err := easyjson.Marshal(benchmarks.NewSmallPayload()); err != nil { b.Fatal(err) } } } func BenchmarkJsonIterEncodeSmallStruct(b *testing.B) { var json = jsoniter.ConfigCompatibleWithStandardLibrary b.ReportAllocs() for i := 0; i < b.N; i++ { if _, err := json.Marshal(benchmarks.NewSmallPayload()); err != nil { b.Fatal(err) } } } func BenchmarkGoJayEncodeSmallStruct(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { if _, err := gojay.MarshalJSONObject(benchmarks.NewSmallPayload()); err != nil { b.Fatal(err) } } } func BenchmarkGoJayEncodeSmallFunc(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { if _, err := gojay.MarshalJSONObject(gojay.EncodeObjectFunc(func(enc *gojay.Encoder) { enc.AddIntKey("st", 1) enc.AddIntKey("sid", 1) enc.AddStringKey("tt", "test") enc.AddIntKey("gr", 1) enc.AddStringKey("uuid", "test") enc.AddStringKey("ip", "test") enc.AddStringKey("ua", "test") enc.AddIntKey("tz", 1) enc.AddIntKey("v", 1) })); err != nil { b.Fatal(err) } } } func TestGoJayEncodeSmallStruct(t *testing.T) { if output, err := gojay.MarshalJSONObject(benchmarks.NewSmallPayload()); err != nil { t.Fatal(err) } else { log.Print(output) } } golang-github-francoispqt-gojay-1.2.13/benchmarks/encoder/encoder_large_test.go000066400000000000000000000000231365331665100277020ustar00rootroot00000000000000package benchmarks golang-github-francoispqt-gojay-1.2.13/benchmarks/encoder/encoder_medium_test.go000066400000000000000000000000231365331665100300700ustar00rootroot00000000000000package benchmarks golang-github-francoispqt-gojay-1.2.13/benchmarks/encoder/encoder_small_test.go000066400000000000000000000000231365331665100277200ustar00rootroot00000000000000package benchmarks golang-github-francoispqt-gojay-1.2.13/decode.go000066400000000000000000000236101365331665100215500ustar00rootroot00000000000000package gojay import ( "fmt" "io" ) // UnmarshalJSONArray parses the JSON-encoded data and stores the result in the value pointed to by v. // // v must implement UnmarshalerJSONArray. // // If a JSON value is not appropriate for a given target type, or if a JSON number // overflows the target type, UnmarshalJSONArray skips that field and completes the unmarshaling as best it can. func UnmarshalJSONArray(data []byte, v UnmarshalerJSONArray) error { dec := borrowDecoder(nil, 0) defer dec.Release() dec.data = make([]byte, len(data)) copy(dec.data, data) dec.length = len(data) _, err := dec.decodeArray(v) if err != nil { return err } if dec.err != nil { return dec.err } return nil } // UnmarshalJSONObject parses the JSON-encoded data and stores the result in the value pointed to by v. // // v must implement UnmarshalerJSONObject. // // If a JSON value is not appropriate for a given target type, or if a JSON number // overflows the target type, UnmarshalJSONObject skips that field and completes the unmarshaling as best it can. func UnmarshalJSONObject(data []byte, v UnmarshalerJSONObject) error { dec := borrowDecoder(nil, 0) defer dec.Release() dec.data = make([]byte, len(data)) copy(dec.data, data) dec.length = len(data) _, err := dec.decodeObject(v) if err != nil { return err } if dec.err != nil { return dec.err } return nil } // Unmarshal parses the JSON-encoded data and stores the result in the value pointed to by v. // If v is nil, not an implementation of UnmarshalerJSONObject or UnmarshalerJSONArray or not one of the following types: // *string, **string, *int, **int, *int8, **int8, *int16, **int16, *int32, **int32, *int64, **int64, *uint8, **uint8, *uint16, **uint16, // *uint32, **uint32, *uint64, **uint64, *float64, **float64, *float32, **float32, *bool, **bool // Unmarshal returns an InvalidUnmarshalError. // // // If a JSON value is not appropriate for a given target type, or if a JSON number // overflows the target type, Unmarshal skips that field and completes the unmarshaling as best it can. // If no more serious errors are encountered, Unmarshal returns an UnmarshalTypeError describing the earliest such error. // In any case, it's not guaranteed that all the remaining fields following the problematic one will be unmarshaled into the target object. func Unmarshal(data []byte, v interface{}) error { var err error var dec *Decoder switch vt := v.(type) { case *string: dec = borrowDecoder(nil, 0) dec.length = len(data) dec.data = data err = dec.decodeString(vt) case **string: dec = borrowDecoder(nil, 0) dec.length = len(data) dec.data = data err = dec.decodeStringNull(vt) case *int: dec = borrowDecoder(nil, 0) dec.length = len(data) dec.data = data err = dec.decodeInt(vt) case **int: dec = borrowDecoder(nil, 0) dec.length = len(data) dec.data = data err = dec.decodeIntNull(vt) case *int8: dec = borrowDecoder(nil, 0) dec.length = len(data) dec.data = data err = dec.decodeInt8(vt) case **int8: dec = borrowDecoder(nil, 0) dec.length = len(data) dec.data = data err = dec.decodeInt8Null(vt) case *int16: dec = borrowDecoder(nil, 0) dec.length = len(data) dec.data = data err = dec.decodeInt16(vt) case **int16: dec = borrowDecoder(nil, 0) dec.length = len(data) dec.data = data err = dec.decodeInt16Null(vt) case *int32: dec = borrowDecoder(nil, 0) dec.length = len(data) dec.data = data err = dec.decodeInt32(vt) case **int32: dec = borrowDecoder(nil, 0) dec.length = len(data) dec.data = data err = dec.decodeInt32Null(vt) case *int64: dec = borrowDecoder(nil, 0) dec.length = len(data) dec.data = data err = dec.decodeInt64(vt) case **int64: dec = borrowDecoder(nil, 0) dec.length = len(data) dec.data = data err = dec.decodeInt64Null(vt) case *uint8: dec = borrowDecoder(nil, 0) dec.length = len(data) dec.data = data err = dec.decodeUint8(vt) case **uint8: dec = borrowDecoder(nil, 0) dec.length = len(data) dec.data = data err = dec.decodeUint8Null(vt) case *uint16: dec = borrowDecoder(nil, 0) dec.length = len(data) dec.data = data err = dec.decodeUint16(vt) case **uint16: dec = borrowDecoder(nil, 0) dec.length = len(data) dec.data = data err = dec.decodeUint16Null(vt) case *uint32: dec = borrowDecoder(nil, 0) dec.length = len(data) dec.data = data err = dec.decodeUint32(vt) case **uint32: dec = borrowDecoder(nil, 0) dec.length = len(data) dec.data = data err = dec.decodeUint32Null(vt) case *uint64: dec = borrowDecoder(nil, 0) dec.length = len(data) dec.data = data err = dec.decodeUint64(vt) case **uint64: dec = borrowDecoder(nil, 0) dec.length = len(data) dec.data = data err = dec.decodeUint64Null(vt) case *float64: dec = borrowDecoder(nil, 0) dec.length = len(data) dec.data = data err = dec.decodeFloat64(vt) case **float64: dec = borrowDecoder(nil, 0) dec.length = len(data) dec.data = data err = dec.decodeFloat64Null(vt) case *float32: dec = borrowDecoder(nil, 0) dec.length = len(data) dec.data = data err = dec.decodeFloat32(vt) case **float32: dec = borrowDecoder(nil, 0) dec.length = len(data) dec.data = data err = dec.decodeFloat32Null(vt) case *bool: dec = borrowDecoder(nil, 0) dec.length = len(data) dec.data = data err = dec.decodeBool(vt) case **bool: dec = borrowDecoder(nil, 0) dec.length = len(data) dec.data = data err = dec.decodeBoolNull(vt) case UnmarshalerJSONObject: dec = borrowDecoder(nil, 0) dec.length = len(data) dec.data = make([]byte, len(data)) copy(dec.data, data) _, err = dec.decodeObject(vt) case UnmarshalerJSONArray: dec = borrowDecoder(nil, 0) dec.length = len(data) dec.data = make([]byte, len(data)) copy(dec.data, data) _, err = dec.decodeArray(vt) case *interface{}: dec = borrowDecoder(nil, 0) dec.length = len(data) dec.data = make([]byte, len(data)) copy(dec.data, data) err = dec.decodeInterface(vt) default: return InvalidUnmarshalError(fmt.Sprintf(invalidUnmarshalErrorMsg, vt)) } defer dec.Release() if err != nil { return err } return dec.err } // UnmarshalerJSONObject is the interface to implement to decode a JSON Object. type UnmarshalerJSONObject interface { UnmarshalJSONObject(*Decoder, string) error NKeys() int } // UnmarshalerJSONArray is the interface to implement to decode a JSON Array. type UnmarshalerJSONArray interface { UnmarshalJSONArray(*Decoder) error } // A Decoder reads and decodes JSON values from an input stream. type Decoder struct { r io.Reader data []byte err error isPooled byte called byte child byte cursor int length int keysDone int arrayIndex int } // Decode reads the next JSON-encoded value from the decoder's input (io.Reader) and stores it in the value pointed to by v. // // See the documentation for Unmarshal for details about the conversion of JSON into a Go value. // The differences between Decode and Unmarshal are: // - Decode reads from an io.Reader in the Decoder, whereas Unmarshal reads from a []byte // - Decode leaves to the user the option of borrowing and releasing a Decoder, whereas Unmarshal internally always borrows a Decoder and releases it when the unmarshaling is completed func (dec *Decoder) Decode(v interface{}) error { if dec.isPooled == 1 { panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) } var err error switch vt := v.(type) { case *string: err = dec.decodeString(vt) case **string: err = dec.decodeStringNull(vt) case *int: err = dec.decodeInt(vt) case **int: err = dec.decodeIntNull(vt) case *int8: err = dec.decodeInt8(vt) case **int8: err = dec.decodeInt8Null(vt) case *int16: err = dec.decodeInt16(vt) case **int16: err = dec.decodeInt16Null(vt) case *int32: err = dec.decodeInt32(vt) case **int32: err = dec.decodeInt32Null(vt) case *int64: err = dec.decodeInt64(vt) case **int64: err = dec.decodeInt64Null(vt) case *uint8: err = dec.decodeUint8(vt) case **uint8: err = dec.decodeUint8Null(vt) case *uint16: err = dec.decodeUint16(vt) case **uint16: err = dec.decodeUint16Null(vt) case *uint32: err = dec.decodeUint32(vt) case **uint32: err = dec.decodeUint32Null(vt) case *uint64: err = dec.decodeUint64(vt) case **uint64: err = dec.decodeUint64Null(vt) case *float64: err = dec.decodeFloat64(vt) case **float64: err = dec.decodeFloat64Null(vt) case *float32: err = dec.decodeFloat32(vt) case **float32: err = dec.decodeFloat32Null(vt) case *bool: err = dec.decodeBool(vt) case **bool: err = dec.decodeBoolNull(vt) case UnmarshalerJSONObject: _, err = dec.decodeObject(vt) case UnmarshalerJSONArray: _, err = dec.decodeArray(vt) case *EmbeddedJSON: err = dec.decodeEmbeddedJSON(vt) case *interface{}: err = dec.decodeInterface(vt) default: return InvalidUnmarshalError(fmt.Sprintf(invalidUnmarshalErrorMsg, vt)) } if err != nil { return err } return dec.err } // Non exported func isDigit(b byte) bool { switch b { case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': return true default: return false } } func (dec *Decoder) read() bool { if dec.r != nil { // if we reach the end, double the buffer to ensure there's always more space if len(dec.data) == dec.length { nLen := dec.length * 2 if nLen == 0 { nLen = 512 } Buf := make([]byte, nLen, nLen) copy(Buf, dec.data) dec.data = Buf } var n int var err error for n == 0 { n, err = dec.r.Read(dec.data[dec.length:]) if err != nil { if err != io.EOF { dec.err = err return false } if n == 0 { return false } dec.length = dec.length + n return true } } dec.length = dec.length + n return true } return false } func (dec *Decoder) nextChar() byte { for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { switch dec.data[dec.cursor] { case ' ', '\n', '\t', '\r', ',': continue } d := dec.data[dec.cursor] return d } return 0 } golang-github-francoispqt-gojay-1.2.13/decode_array.go000066400000000000000000000150521365331665100227470ustar00rootroot00000000000000package gojay import "reflect" // DecodeArray reads the next JSON-encoded value from the decoder's input (io.Reader) // and stores it in the value pointed to by v. // // v must implement UnmarshalerJSONArray. // // See the documentation for Unmarshal for details about the conversion of JSON into a Go value. func (dec *Decoder) DecodeArray(v UnmarshalerJSONArray) error { if dec.isPooled == 1 { panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) } _, err := dec.decodeArray(v) return err } func (dec *Decoder) decodeArray(arr UnmarshalerJSONArray) (int, error) { // remember last array index in case of nested arrays lastArrayIndex := dec.arrayIndex dec.arrayIndex = 0 defer func() { dec.arrayIndex = lastArrayIndex }() for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { switch dec.data[dec.cursor] { case ' ', '\n', '\t', '\r', ',': continue case '[': dec.cursor = dec.cursor + 1 // array is open, char is not space start readings for dec.nextChar() != 0 { // closing array if dec.data[dec.cursor] == ']' { dec.cursor = dec.cursor + 1 return dec.cursor, nil } // calling unmarshall function for each element of the slice err := arr.UnmarshalJSONArray(dec) if err != nil { return 0, err } dec.arrayIndex++ } return 0, dec.raiseInvalidJSONErr(dec.cursor) case 'n': // is null dec.cursor++ err := dec.assertNull() if err != nil { return 0, err } return dec.cursor, nil case '{', '"', 'f', 't', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': // can't unmarshall to struct // we skip array and set Error dec.err = dec.makeInvalidUnmarshalErr(arr) err := dec.skipData() if err != nil { return 0, err } return dec.cursor, nil default: return 0, dec.raiseInvalidJSONErr(dec.cursor) } } return 0, dec.raiseInvalidJSONErr(dec.cursor) } func (dec *Decoder) decodeArrayNull(v interface{}) (int, error) { // remember last array index in case of nested arrays lastArrayIndex := dec.arrayIndex dec.arrayIndex = 0 defer func() { dec.arrayIndex = lastArrayIndex }() vv := reflect.ValueOf(v) vvt := vv.Type() if vvt.Kind() != reflect.Ptr || vvt.Elem().Kind() != reflect.Ptr { dec.err = ErrUnmarshalPtrExpected return 0, dec.err } // not an array not an error, but do not know what to do // do not check syntax for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { switch dec.data[dec.cursor] { case ' ', '\n', '\t', '\r', ',': continue case '[': dec.cursor = dec.cursor + 1 // create our new type elt := vv.Elem() n := reflect.New(elt.Type().Elem()) var arr UnmarshalerJSONArray var ok bool if arr, ok = n.Interface().(UnmarshalerJSONArray); !ok { dec.err = dec.makeInvalidUnmarshalErr((UnmarshalerJSONArray)(nil)) return 0, dec.err } // array is open, char is not space start readings for dec.nextChar() != 0 { // closing array if dec.data[dec.cursor] == ']' { elt.Set(n) dec.cursor = dec.cursor + 1 return dec.cursor, nil } // calling unmarshall function for each element of the slice err := arr.UnmarshalJSONArray(dec) if err != nil { return 0, err } dec.arrayIndex++ } return 0, dec.raiseInvalidJSONErr(dec.cursor) case 'n': // is null dec.cursor++ err := dec.assertNull() if err != nil { return 0, err } return dec.cursor, nil case '{', '"', 'f', 't', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': // can't unmarshall to struct // we skip array and set Error dec.err = dec.makeInvalidUnmarshalErr((UnmarshalerJSONArray)(nil)) err := dec.skipData() if err != nil { return 0, err } return dec.cursor, nil default: return 0, dec.raiseInvalidJSONErr(dec.cursor) } } return 0, dec.raiseInvalidJSONErr(dec.cursor) } func (dec *Decoder) skipArray() (int, error) { var arraysOpen = 1 var arraysClosed = 0 // var stringOpen byte = 0 for j := dec.cursor; j < dec.length || dec.read(); j++ { switch dec.data[j] { case ']': arraysClosed++ // everything is closed return if arraysOpen == arraysClosed { // add char to object data return j + 1, nil } case '[': arraysOpen++ case '"': j++ var isInEscapeSeq bool var isFirstQuote = true for ; j < dec.length || dec.read(); j++ { if dec.data[j] != '"' { continue } if dec.data[j-1] != '\\' || (!isInEscapeSeq && !isFirstQuote) { break } else { isInEscapeSeq = false } if isFirstQuote { isFirstQuote = false } // loop backward and count how many anti slash found // to see if string is effectively escaped ct := 0 for i := j - 1; i > 0; i-- { if dec.data[i] != '\\' { break } ct++ } // is pair number of slashes, quote is not escaped if ct&1 == 0 { break } isInEscapeSeq = true } default: continue } } return 0, dec.raiseInvalidJSONErr(dec.cursor) } // DecodeArrayFunc is a func type implementing UnmarshalerJSONArray. // Use it to cast a `func(*Decoder) error` to Unmarshal an array on the fly. type DecodeArrayFunc func(*Decoder) error // UnmarshalJSONArray implements UnmarshalerJSONArray. func (f DecodeArrayFunc) UnmarshalJSONArray(dec *Decoder) error { return f(dec) } // IsNil implements UnmarshalerJSONArray. func (f DecodeArrayFunc) IsNil() bool { return f == nil } // Add Values functions // AddArray decodes the JSON value within an object or an array to a UnmarshalerJSONArray. func (dec *Decoder) AddArray(v UnmarshalerJSONArray) error { return dec.Array(v) } // AddArrayNull decodes the JSON value within an object or an array to a UnmarshalerJSONArray. func (dec *Decoder) AddArrayNull(v interface{}) error { return dec.ArrayNull(v) } // Array decodes the JSON value within an object or an array to a UnmarshalerJSONArray. func (dec *Decoder) Array(v UnmarshalerJSONArray) error { newCursor, err := dec.decodeArray(v) if err != nil { return err } dec.cursor = newCursor dec.called |= 1 return nil } // ArrayNull decodes the JSON value within an object or an array to a UnmarshalerJSONArray. // v should be a pointer to an UnmarshalerJSONArray, // if `null` value is encountered in JSON, it will leave the value v untouched, // else it will create a new instance of the UnmarshalerJSONArray behind v. func (dec *Decoder) ArrayNull(v interface{}) error { newCursor, err := dec.decodeArrayNull(v) if err != nil { return err } dec.cursor = newCursor dec.called |= 1 return nil } // Index returns the index of an array being decoded. func (dec *Decoder) Index() int { return dec.arrayIndex } golang-github-francoispqt-gojay-1.2.13/decode_array_test.go000066400000000000000000000464101365331665100240100ustar00rootroot00000000000000package gojay import ( "strings" "testing" "github.com/stretchr/testify/assert" ) type testSliceInts []int func (t *testSliceInts) UnmarshalJSONArray(dec *Decoder) error { i := 0 if err := dec.AddInt(&i); err != nil { return err } *t = append(*t, i) return nil } func TestSliceInts(t *testing.T) { testCases := []struct { name string json string expectedResult testSliceInts err bool errType interface{} }{ { name: "basic-test", json: "[1,2,3,43567788543,45777655,432,0]", expectedResult: testSliceInts{1, 2, 3, 43567788543, 45777655, 432, 0}, }, { name: "basic-test", json: "[1,2,3,43567788543,null,432,0]", expectedResult: testSliceInts{1, 2, 3, 43567788543, 0, 432, 0}, }, { name: "empty", json: "[]", expectedResult: testSliceInts{}, }, { name: "invalid-json", json: "[", expectedResult: testSliceInts{}, err: true, }, { name: "floats", json: "[1,2,3,43567788543,457.7765,432,0,0.45]", expectedResult: testSliceInts{1, 2, 3, 43567788543, 457, 432, 0, 0}, }, { name: "invalid-type", json: `[1,2,3,43567788543,457.7765,432,0,"test"]`, expectedResult: testSliceInts{1, 2, 3, 43567788543, 457, 432, 0, 0}, err: true, errType: InvalidUnmarshalError(""), }, { name: "invalid-json", json: `[1,2,3",43567788543,457.7765,432,0,"test"]`, expectedResult: testSliceInts{1, 2, 3, 43567788543, 457, 432, 0, 0}, err: true, errType: InvalidJSONError(""), }, } for _, testCase := range testCases { s := make(testSliceInts, 0) dec := BorrowDecoder(strings.NewReader(testCase.json)) defer dec.Release() err := dec.Decode(&s) if testCase.err { assert.NotNil(t, err, "err should not be nil") if testCase.errType != nil { assert.IsType(t, testCase.errType, err, "err should be of the given type") } continue } for k, v := range testCase.expectedResult { assert.Equal(t, v, s[k], "value at given index should be the same as expected results") } } } type testSliceStrings []string func (t *testSliceStrings) UnmarshalJSONArray(dec *Decoder) error { str := "" if err := dec.AddString(&str); err != nil { return err } *t = append(*t, str) return nil } func TestSliceStrings(t *testing.T) { testCases := []struct { name string json string expectedResult testSliceStrings err bool errType interface{} }{ { name: "basic-test", json: `["hello world", "hey" , "foo","bar"]`, expectedResult: testSliceStrings{"hello world", "hey", "foo", "bar"}, }, { name: "basic-test", json: `["hello world", "hey" , "foo","bar \n escape"]`, expectedResult: testSliceStrings{"hello world", "hey", "foo", "bar \n escape"}, }, { name: "basic-test", json: `["hello world", "hey" , null,"bar \n escape"]`, expectedResult: testSliceStrings{"hello world", "hey", "", "bar \n escape"}, }, { name: "invalid-type", json: `["foo",1,2,3,"test"]`, expectedResult: testSliceStrings{}, err: true, errType: InvalidUnmarshalError(""), }, { name: "invalid-json", json: `["hello world]`, expectedResult: testSliceStrings{}, err: true, errType: InvalidJSONError(""), }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { s := make(testSliceStrings, 0) dec := BorrowDecoder(strings.NewReader(testCase.json)) defer dec.Release() err := dec.Decode(&s) if testCase.err { assert.NotNil(t, err, "err should not be nil") if testCase.errType != nil { assert.IsType(t, testCase.errType, err, "err should be of the given type") } return } assert.Nil(t, err, "err should be nil") for k, v := range testCase.expectedResult { assert.Equal(t, v, s[k], "value at given index should be the same as expected results") } }) } } type testSliceBools []bool func (t *testSliceBools) UnmarshalJSONArray(dec *Decoder) error { b := false if err := dec.AddBool(&b); err != nil { return err } *t = append(*t, b) return nil } func TestSliceBools(t *testing.T) { testCases := []struct { name string json string expectedResult testSliceBools err bool errType interface{} }{ { name: "basic-test", json: `[true, false, false, true, true, false]`, expectedResult: testSliceBools{true, false, false, true, true, false}, }, { name: "basic-test2", json: `[true, false, false, true, null,null,true,false]`, expectedResult: testSliceBools{true, false, false, true, false, false, true, false}, }, { name: "invalid-type", json: `["foo",1,2,3,"test"]`, expectedResult: testSliceBools{}, err: true, errType: InvalidUnmarshalError(""), }, { name: "invalid-json", json: `["hello world]`, expectedResult: testSliceBools{}, err: true, errType: InvalidJSONError(""), }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { s := make(testSliceBools, 0) dec := BorrowDecoder(strings.NewReader(testCase.json)) defer dec.Release() err := dec.Decode(&s) if testCase.err { assert.NotNil(t, err, "err should not be nil") if testCase.errType != nil { assert.IsType(t, testCase.errType, err, "err should be of the given type") } return } assert.Nil(t, err, "err should be nil") for k, v := range testCase.expectedResult { assert.Equal(t, v, s[k], "value at given index should be the same as expected results") } }) } } type testSliceSlicesSlices []testSliceInts func (t *testSliceSlicesSlices) UnmarshalJSONArray(dec *Decoder) error { sl := make(testSliceInts, 0) if err := dec.AddArray(&sl); err != nil { return err } *t = append(*t, sl) return nil } func TestSliceSlices(t *testing.T) { testCases := []struct { name string json string expectedResult testSliceSlicesSlices err bool errType interface{} }{ { name: "basic-test", json: `[[1,2],[1,2],[1,2]]`, expectedResult: testSliceSlicesSlices{testSliceInts{1, 2}, testSliceInts{1, 2}, testSliceInts{1, 2}}, }, { name: "basic-test", json: `[[1,2],null,[1,2]]`, expectedResult: testSliceSlicesSlices{testSliceInts{1, 2}, testSliceInts{}, testSliceInts{1, 2}}, }, { name: "invalid-type", json: `["foo",1,2,3,"test"]`, expectedResult: testSliceSlicesSlices{}, err: true, errType: InvalidUnmarshalError(""), }, { name: "invalid-json", json: `["hello world]`, expectedResult: testSliceSlicesSlices{}, err: true, errType: InvalidJSONError(""), }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { s := make(testSliceSlicesSlices, 0) dec := BorrowDecoder(strings.NewReader(testCase.json)) defer dec.Release() err := dec.Decode(&s) if testCase.err { assert.NotNil(t, err, "err should not be nil") if testCase.errType != nil { assert.IsType(t, testCase.errType, err, "err should be of the given type") } return } assert.Nil(t, err, "err should be nil") for k, v := range testCase.expectedResult { assert.Equal(t, v, s[k], "value at given index should be the same as expected results") } }) } } type testSliceObjects []*testObject func (t *testSliceObjects) UnmarshalJSONArray(dec *Decoder) error { obj := &testObject{} *t = append(*t, obj) return dec.AddObject(obj) } func TestSliceObjects(t *testing.T) { testCases := []struct { name string json string expectedResult testSliceObjects err bool errType interface{} }{ { name: "basic-test", json: `[{"testStr":"foo bar","testInt":123},{"testStr":"foo bar","testInt":123}]`, expectedResult: testSliceObjects{ &testObject{ testStr: "foo bar", testInt: 123, }, &testObject{ testStr: "foo bar", testInt: 123, }, }, }, { name: "basic-test", json: `[{"testStr":"foo bar","testInt":123},null,{"testStr":"foo bar","testInt":123}]`, expectedResult: testSliceObjects{ &testObject{ testStr: "foo bar", testInt: 123, }, &testObject{}, &testObject{ testStr: "foo bar", testInt: 123, }, }, }, { name: "invalid-type", json: `["foo",1,2,3,"test"]`, expectedResult: testSliceObjects{}, err: true, errType: InvalidUnmarshalError(""), }, { name: "invalid-json", json: `["hello world]`, expectedResult: testSliceObjects{}, err: true, errType: InvalidJSONError(""), }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { s := make(testSliceObjects, 0) dec := BorrowDecoder(strings.NewReader(testCase.json)) defer dec.Release() err := dec.Decode(&s) if testCase.err { assert.NotNil(t, err, "err should not be nil") if testCase.errType != nil { assert.IsType(t, testCase.errType, err, "err should be of the given type") } return } assert.Nil(t, err, "err should be nil") for k, v := range testCase.expectedResult { assert.Equal(t, *v, *s[k], "value at given index should be the same as expected results") } }) } } type ArrayNull []string func (a *ArrayNull) UnmarshalJSONArray(dec *Decoder) error { var str string if err := dec.String(&str); err != nil { return err } *a = append(*a, str) return nil } type ObjectArrayNull struct { SubArray *ArrayNull } func (o *ObjectArrayNull) UnmarshalJSONObject(dec *Decoder, k string) error { switch k { case "subarray": return dec.ArrayNull(&o.SubArray) } return nil } func (o *ObjectArrayNull) NKeys() int { return 1 } func TestDecodeArrayNullPtr(t *testing.T) { t.Run("sub obj should not be nil", func(t *testing.T) { var o = &ObjectArrayNull{} var err = UnmarshalJSONObject([]byte(`{"subarray": ["test"]}`), o) assert.Nil(t, err) assert.NotNil(t, o.SubArray) assert.Len(t, *o.SubArray, 1) }) t.Run("sub array should be nil", func(t *testing.T) { var o = &ObjectArrayNull{} var err = UnmarshalJSONObject([]byte(`{"subarray": null}`), o) assert.Nil(t, err) assert.Nil(t, o.SubArray) }) t.Run("sub array err, not closing arr", func(t *testing.T) { var o = &ObjectArrayNull{} var err = UnmarshalJSONObject([]byte(`{"subarray": [ `), o) assert.NotNil(t, err) }) t.Run("sub array err, invalid string", func(t *testing.T) { var o = &ObjectArrayNull{} var err = UnmarshalJSONObject([]byte(`{"subarray":[",]}`), o) assert.NotNil(t, err) }) t.Run("sub array err, invalid null", func(t *testing.T) { var o = &ObjectArrayNull{} var err = UnmarshalJSONObject([]byte(`{"subarray":nll}`), o) assert.NotNil(t, err) }) t.Run("sub array err, empty", func(t *testing.T) { var o = &ObjectArrayNull{} var err = UnmarshalJSONObject([]byte(`{"subarray":`), o) assert.NotNil(t, err) }) } type testChannelArray chan *TestObj func (c *testChannelArray) UnmarshalJSONArray(dec *Decoder) error { obj := &TestObj{} if err := dec.AddObject(obj); err != nil { return err } *c <- obj return nil } func TestDecoderSliceNull(t *testing.T) { json := []byte(`null`) v := &testSliceStrings{} err := Unmarshal(json, v) assert.Nil(t, err, "Err must be nil") assert.Equal(t, len(*v), 0, "v must be of len 0") } func TestDecodeSliceInvalidType(t *testing.T) { result := testSliceObjects{} err := UnmarshalJSONArray([]byte(`{}`), &result) assert.NotNil(t, err, "err should not be nil") assert.IsType(t, InvalidUnmarshalError(""), err, "err should be of type InvalidUnmarshalError") assert.Equal(t, "Cannot unmarshal JSON to type '*gojay.testSliceObjects'", err.Error(), "err should not be nil") } func TestDecoderChannelOfObjectsBasic(t *testing.T) { json := []byte(`[ { "test": 245, "test2": -246, "test3": "string" }, { "test": 247, "test2": 248, "test3": "string" }, { "test": 777, "test2": 456, "test3": "string" } ]`) testChan := testChannelArray(make(chan *TestObj, 3)) err := UnmarshalJSONArray(json, &testChan) assert.Nil(t, err, "Err must be nil") ct := 0 l := len(testChan) for range testChan { ct++ if ct == l { break } } assert.Equal(t, ct, 3) } func TestDecoderSliceInvalidJSON(t *testing.T) { json := []byte(`hello`) testArr := testSliceInts{} err := UnmarshalJSONArray(json, &testArr) assert.NotNil(t, err, "Err must not be nil as JSON is invalid") assert.IsType(t, InvalidJSONError(""), err, "err message must be 'Invalid JSON'") } func TestDecoderSliceDecoderAPI(t *testing.T) { json := `["string","string1"]` testArr := testSliceStrings{} dec := NewDecoder(strings.NewReader(json)) err := dec.DecodeArray(&testArr) assert.Nil(t, err, "Err must be nil") assert.Len(t, testArr, 2, "testArr should be of len 2") assert.Equal(t, "string", testArr[0], "testArr[0] should be 'string'") assert.Equal(t, "string1", testArr[1], "testArr[1] should be 'string1'") } func TestDecoderSliceDecoderAPIError(t *testing.T) { testArr := testSliceInts{} dec := NewDecoder(strings.NewReader(`hello`)) err := dec.DecodeArray(&testArr) assert.NotNil(t, err, "Err must not be nil as JSON is invalid") assert.IsType(t, InvalidJSONError(""), err, "err message must be 'Invalid JSON'") } func TestUnmarshalJSONArrays(t *testing.T) { testCases := []struct { name string v UnmarshalerJSONArray d []byte expectations func(err error, v interface{}, t *testing.T) }{ { v: new(testDecodeSlice), d: []byte(`[{"test":"test"}]`), name: "test decode slice", expectations: func(err error, v interface{}, t *testing.T) { vtPtr := v.(*testDecodeSlice) vt := *vtPtr assert.Nil(t, err, "err must be nil") assert.Len(t, vt, 1, "len of vt must be 1") assert.Equal(t, "test", vt[0].test, "vt[0].test must be equal to 'test'") }, }, { v: new(testDecodeSlice), d: []byte(`[{"test":"test"},{"test":"test2"}]`), name: "test decode slice", expectations: func(err error, v interface{}, t *testing.T) { vtPtr := v.(*testDecodeSlice) vt := *vtPtr assert.Nil(t, err, "err must be nil") assert.Len(t, vt, 2, "len of vt must be 2") assert.Equal(t, "test", vt[0].test, "vt[0].test must be equal to 'test'") assert.Equal(t, "test2", vt[1].test, "vt[1].test must be equal to 'test2'") }, }, { v: new(testDecodeSlice), d: []byte(`invalid json`), name: "test decode object null", expectations: func(err error, v interface{}, t *testing.T) { assert.NotNil(t, err, "err must not be nil") assert.IsType(t, InvalidJSONError(""), err, "err must be of type InvalidJSONError") }, }, } for _, testCase := range testCases { testCase := testCase t.Run(testCase.name, func(*testing.T) { err := UnmarshalJSONArray(testCase.d, testCase.v) testCase.expectations(err, testCase.v, t) }) } } func TestSkipArray(t *testing.T) { testCases := []struct { name string json string expectations func(*testing.T, int, error) }{ { name: "basic", json: `"testbasic"]`, expectations: func(t *testing.T, i int, err error) { assert.Equal(t, len(`"testbasic"]`), i) assert.Nil(t, err) }, }, { name: "complex escape string", json: `"test \\\\\" escape"]`, expectations: func(t *testing.T, i int, err error) { assert.Equal(t, len(`"test \\\\\" escape"]`), i) assert.Nil(t, err) }, }, { name: "complex escape slash", json: `"test \\\\\\"]`, expectations: func(t *testing.T, i int, err error) { assert.Equal(t, len(`"test \\\\\\"]`), i) assert.Nil(t, err) }, }, { json: `"test \n"]`, expectations: func(t *testing.T, i int, err error) { assert.Equal(t, len(`"test \n"]`), i) assert.Nil(t, err) }, }, } for _, test := range testCases { t.Run(test.name, func(t *testing.T) { dec := NewDecoder(strings.NewReader(test.json)) i, err := dec.skipArray() test.expectations(t, i, err) }) } } func TestDecodeArrayEmpty(t *testing.T) { v := new(testDecodeSlice) dec := NewDecoder(strings.NewReader("")) err := dec.Decode(v) assert.NotNil(t, err, "err should not be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") } func TestDecodeArraySkipError(t *testing.T) { v := new(testDecodeSlice) dec := NewDecoder(strings.NewReader("34fef")) err := dec.Decode(v) assert.NotNil(t, err, "err should not be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") } func TestDecodeArrayNullError(t *testing.T) { v := new(testDecodeSlice) dec := NewDecoder(strings.NewReader("nall")) err := dec.Decode(v) assert.NotNil(t, err, "err should not be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") } func TestDecoderArrayFunc(t *testing.T) { var f DecodeArrayFunc assert.True(t, f.IsNil()) } type testArrayStrings [3]string func (a *testArrayStrings) UnmarshalJSONArray(dec *Decoder) error { var str string if err := dec.String(&str); err != nil { return err } a[dec.Index()] = str return nil } func TestArrayStrings(t *testing.T) { data := []byte(`["a", "b", "c"]`) arr := testArrayStrings{} err := Unmarshal(data, &arr) assert.Nil(t, err, "err must be nil") assert.Equal(t, "a", arr[0], "arr[0] must be equal to 'a'") assert.Equal(t, "b", arr[1], "arr[1] must be equal to 'b'") assert.Equal(t, "c", arr[2], "arr[2] must be equal to 'c'") } type testSliceArraysStrings struct { arrays []testArrayStrings t *testing.T } func (s *testSliceArraysStrings) UnmarshalJSONArray(dec *Decoder) error { var a testArrayStrings assert.Equal(s.t, len(s.arrays), dec.Index(), "decoded array index must be equal to current slice len") if err := dec.AddArray(&a); err != nil { return err } assert.Equal(s.t, len(s.arrays), dec.Index(), "decoded array index must be equal to current slice len") s.arrays = append(s.arrays, a) return nil } func TestIndex(t *testing.T) { testCases := []struct { name string json string expectedResult []testArrayStrings }{ { name: "basic-test", json: `[["a","b","c"],["1","2","3"],["x","y","z"]]`, expectedResult: []testArrayStrings{{"a", "b", "c"}, {"1", "2", "3"}, {"x", "y", "z"}}, }, { name: "basic-test-null", json: `[["a","b","c"],null,["x","y","z"]]`, expectedResult: []testArrayStrings{{"a", "b", "c"}, {"", "", ""}, {"x", "y", "z"}}, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { s := make([]testArrayStrings, 0) dec := BorrowDecoder(strings.NewReader(testCase.json)) defer dec.Release() a := testSliceArraysStrings{arrays: s, t: t} err := dec.Decode(&a) assert.Nil(t, err, "err should be nil") assert.Zero(t, dec.Index(), "Index() must return zero after decoding") for k, v := range testCase.expectedResult { assert.Equal(t, v, a.arrays[k], "value at given index should be the same as expected results") } }) } } golang-github-francoispqt-gojay-1.2.13/decode_bool.go000066400000000000000000000125331365331665100225650ustar00rootroot00000000000000package gojay // DecodeBool reads the next JSON-encoded value from the decoder's input (io.Reader) // and stores it in the boolean pointed to by v. // // See the documentation for Unmarshal for details about the conversion of JSON into a Go value. func (dec *Decoder) DecodeBool(v *bool) error { if dec.isPooled == 1 { panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) } return dec.decodeBool(v) } func (dec *Decoder) decodeBool(v *bool) error { for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { switch dec.data[dec.cursor] { case ' ', '\n', '\t', '\r', ',': continue case 't': dec.cursor++ err := dec.assertTrue() if err != nil { return err } *v = true return nil case 'f': dec.cursor++ err := dec.assertFalse() if err != nil { return err } *v = false return nil case 'n': dec.cursor++ err := dec.assertNull() if err != nil { return err } *v = false return nil default: dec.err = dec.makeInvalidUnmarshalErr(v) err := dec.skipData() if err != nil { return err } return nil } } return nil } func (dec *Decoder) decodeBoolNull(v **bool) error { for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { switch dec.data[dec.cursor] { case ' ', '\n', '\t', '\r', ',': continue case 't': dec.cursor++ err := dec.assertTrue() if err != nil { return err } if *v == nil { *v = new(bool) } **v = true return nil case 'f': dec.cursor++ err := dec.assertFalse() if err != nil { return err } if *v == nil { *v = new(bool) } **v = false return nil case 'n': dec.cursor++ err := dec.assertNull() if err != nil { return err } return nil default: dec.err = dec.makeInvalidUnmarshalErr(v) err := dec.skipData() if err != nil { return err } return nil } } return nil } func (dec *Decoder) assertTrue() error { i := 0 for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { switch i { case 0: if dec.data[dec.cursor] != 'r' { return dec.raiseInvalidJSONErr(dec.cursor) } case 1: if dec.data[dec.cursor] != 'u' { return dec.raiseInvalidJSONErr(dec.cursor) } case 2: if dec.data[dec.cursor] != 'e' { return dec.raiseInvalidJSONErr(dec.cursor) } case 3: switch dec.data[dec.cursor] { case ' ', '\b', '\t', '\n', ',', ']', '}': // dec.cursor-- return nil default: return dec.raiseInvalidJSONErr(dec.cursor) } } i++ } if i == 3 { return nil } return dec.raiseInvalidJSONErr(dec.cursor) } func (dec *Decoder) assertNull() error { i := 0 for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { switch i { case 0: if dec.data[dec.cursor] != 'u' { return dec.raiseInvalidJSONErr(dec.cursor) } case 1: if dec.data[dec.cursor] != 'l' { return dec.raiseInvalidJSONErr(dec.cursor) } case 2: if dec.data[dec.cursor] != 'l' { return dec.raiseInvalidJSONErr(dec.cursor) } case 3: switch dec.data[dec.cursor] { case ' ', '\t', '\n', ',', ']', '}': // dec.cursor-- return nil default: return dec.raiseInvalidJSONErr(dec.cursor) } } i++ } if i == 3 { return nil } return dec.raiseInvalidJSONErr(dec.cursor) } func (dec *Decoder) assertFalse() error { i := 0 for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { switch i { case 0: if dec.data[dec.cursor] != 'a' { return dec.raiseInvalidJSONErr(dec.cursor) } case 1: if dec.data[dec.cursor] != 'l' { return dec.raiseInvalidJSONErr(dec.cursor) } case 2: if dec.data[dec.cursor] != 's' { return dec.raiseInvalidJSONErr(dec.cursor) } case 3: if dec.data[dec.cursor] != 'e' { return dec.raiseInvalidJSONErr(dec.cursor) } case 4: switch dec.data[dec.cursor] { case ' ', '\t', '\n', ',', ']', '}': // dec.cursor-- return nil default: return dec.raiseInvalidJSONErr(dec.cursor) } } i++ } if i == 4 { return nil } return dec.raiseInvalidJSONErr(dec.cursor) } // Add Values functions // AddBool decodes the JSON value within an object or an array to a *bool. // If next key is neither null nor a JSON boolean, an InvalidUnmarshalError will be returned. // If next key is null, bool will be false. func (dec *Decoder) AddBool(v *bool) error { return dec.Bool(v) } // AddBoolNull decodes the JSON value within an object or an array to a *bool. // If next key is neither null nor a JSON boolean, an InvalidUnmarshalError will be returned. // If next key is null, bool will be false. // If a `null` is encountered, gojay does not change the value of the pointer. func (dec *Decoder) AddBoolNull(v **bool) error { return dec.BoolNull(v) } // Bool decodes the JSON value within an object or an array to a *bool. // If next key is neither null nor a JSON boolean, an InvalidUnmarshalError will be returned. // If next key is null, bool will be false. func (dec *Decoder) Bool(v *bool) error { err := dec.decodeBool(v) if err != nil { return err } dec.called |= 1 return nil } // BoolNull decodes the JSON value within an object or an array to a *bool. // If next key is neither null nor a JSON boolean, an InvalidUnmarshalError will be returned. // If next key is null, bool will be false. func (dec *Decoder) BoolNull(v **bool) error { err := dec.decodeBoolNull(v) if err != nil { return err } dec.called |= 1 return nil } golang-github-francoispqt-gojay-1.2.13/decode_bool_test.go000066400000000000000000000360521365331665100236260ustar00rootroot00000000000000package gojay import ( "strings" "testing" "github.com/stretchr/testify/assert" ) func TestDecoderBool(t *testing.T) { testCases := []struct { name string json string expectedResult bool expectations func(t *testing.T, v bool, err error) }{ { name: "true-basic", json: "true", expectations: func(t *testing.T, v bool, err error) { assert.Nil(t, err, "err should be nil") assert.True(t, v, "result should be true") }, }, { name: "false-basic", json: "false", expectations: func(t *testing.T, v bool, err error) { assert.Nil(t, err, "err should be nil") assert.False(t, v, "result should be false") }, }, { name: "null-basic", json: "null", expectations: func(t *testing.T, v bool, err error) { assert.Nil(t, err, "err should be nil") assert.False(t, v, "result should be false") }, }, { name: "true-error", json: "taue", expectations: func(t *testing.T, v bool, err error) { assert.NotNil(t, err, "err should be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") assert.False(t, v, "result should be false") }, }, { name: "true-error2", json: "trae", expectations: func(t *testing.T, v bool, err error) { assert.NotNil(t, err, "err should be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") assert.False(t, v, "result should be false") }, }, { name: "true-error3", json: "trua", expectations: func(t *testing.T, v bool, err error) { assert.NotNil(t, err, "err should be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") assert.False(t, v, "result should be false") }, }, { name: "true-error4", json: "truea", expectations: func(t *testing.T, v bool, err error) { assert.NotNil(t, err, "err should be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") assert.False(t, v, "result should be false") }, }, { name: "true-error5", json: "t", expectations: func(t *testing.T, v bool, err error) { assert.NotNil(t, err, "err should be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") assert.False(t, v, "result should be false") }, }, { name: "true-error6", json: "a", expectations: func(t *testing.T, v bool, err error) { assert.NotNil(t, err, "err should be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") assert.False(t, v, "result should be false") }, }, { name: "false-error", json: "fulse", expectations: func(t *testing.T, v bool, err error) { assert.NotNil(t, err, "err should be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") assert.False(t, v, "result should be false") }, }, { name: "false-error2", json: "fause", expectations: func(t *testing.T, v bool, err error) { assert.NotNil(t, err, "err should be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") assert.False(t, v, "result should be false") }, }, { name: "false-error3", json: "falze", expectations: func(t *testing.T, v bool, err error) { assert.NotNil(t, err, "err should be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") assert.False(t, v, "result should be false") }, }, { name: "false-error4", json: "falso", expectations: func(t *testing.T, v bool, err error) { assert.NotNil(t, err, "err should be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") assert.False(t, v, "result should be false") }, }, { name: "false-error5", json: "falsea", expectations: func(t *testing.T, v bool, err error) { assert.NotNil(t, err, "err should be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") assert.False(t, v, "result should be false") }, }, { name: "false-error6", json: "f", expectations: func(t *testing.T, v bool, err error) { assert.NotNil(t, err, "err should be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") assert.False(t, v, "result should be false") }, }, { name: "false-error7", json: "a", expectations: func(t *testing.T, v bool, err error) { assert.NotNil(t, err, "err should be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") assert.False(t, v, "result should be false") }, }, { name: "null-error", json: "nall", expectations: func(t *testing.T, v bool, err error) { assert.NotNil(t, err, "err should be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") assert.False(t, v, "result should be false") }, }, { name: "null-error2", json: "nual", expectations: func(t *testing.T, v bool, err error) { assert.NotNil(t, err, "err should be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") assert.False(t, v, "result should be false") }, }, { name: "null-error3", json: "nula", expectations: func(t *testing.T, v bool, err error) { assert.NotNil(t, err, "err should be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") assert.False(t, v, "result should be false") }, }, { name: "null-error4", json: "nulle", expectations: func(t *testing.T, v bool, err error) { assert.NotNil(t, err, "err should be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") assert.False(t, v, "result should be false") }, }, { name: "null-error5", json: "n", expectations: func(t *testing.T, v bool, err error) { assert.NotNil(t, err, "err should be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") assert.False(t, v, "result should be false") }, }, { name: "null-error6", json: "a", expectations: func(t *testing.T, v bool, err error) { assert.NotNil(t, err, "err should be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") assert.False(t, v, "result should be false") }, }, { name: "null-skip", json: "{}", expectations: func(t *testing.T, v bool, err error) { assert.NotNil(t, err, "err should not be nil") assert.IsType(t, InvalidUnmarshalError(""), err, "err should be of type InvalidUnmarshalError") assert.False(t, v, "result should be false") }, }, { name: "null-skip", json: "", expectations: func(t *testing.T, v bool, err error) { assert.Nil(t, err, "err should not be nil") assert.False(t, v, "result should be false") }, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { json := []byte(testCase.json) var v bool err := Unmarshal(json, &v) testCase.expectations(t, v, err) }) } } func TestDecoderBoolNull(t *testing.T) { testCases := []struct { name string json string expectedResult bool expectations func(t *testing.T, v *bool, err error) }{ { name: "true-basic", json: "true", expectations: func(t *testing.T, v *bool, err error) { assert.Nil(t, err, "err should be nil") assert.True(t, *v, "result should be true") }, }, { name: "false-basic", json: "false", expectations: func(t *testing.T, v *bool, err error) { assert.Nil(t, err, "err should be nil") assert.False(t, *v, "result should be false") }, }, { name: "null-basic", json: "null", expectations: func(t *testing.T, v *bool, err error) { assert.Nil(t, err, "err should be nil") assert.Nil(t, v, "result should be nil") }, }, { name: "true-error", json: "taue", expectations: func(t *testing.T, v *bool, err error) { assert.NotNil(t, err, "err should be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") assert.Nil(t, v, "result should be false") }, }, { name: "true-error2", json: "trae", expectations: func(t *testing.T, v *bool, err error) { assert.NotNil(t, err, "err should be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") assert.Nil(t, v, "result should be nil") }, }, { name: "true-error3", json: "trua", expectations: func(t *testing.T, v *bool, err error) { assert.NotNil(t, err, "err should be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") assert.Nil(t, v, "result should be nil") }, }, { name: "true-error4", json: "truea", expectations: func(t *testing.T, v *bool, err error) { assert.NotNil(t, err, "err should be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") assert.Nil(t, v, "result should be nil") }, }, { name: "true-error5", json: "t", expectations: func(t *testing.T, v *bool, err error) { assert.NotNil(t, err, "err should be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") assert.Nil(t, v, "result should be nil") }, }, { name: "true-error6", json: "a", expectations: func(t *testing.T, v *bool, err error) { assert.NotNil(t, err, "err should be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") assert.Nil(t, v, "result should be nil") }, }, { name: "false-error", json: "fulse", expectations: func(t *testing.T, v *bool, err error) { assert.NotNil(t, err, "err should be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") assert.Nil(t, v, "result should be nil") }, }, { name: "false-error2", json: "fause", expectations: func(t *testing.T, v *bool, err error) { assert.NotNil(t, err, "err should be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") assert.Nil(t, v, "result should be nil") }, }, { name: "false-error3", json: "falze", expectations: func(t *testing.T, v *bool, err error) { assert.NotNil(t, err, "err should be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") assert.Nil(t, v, "result should be nil") }, }, { name: "false-error4", json: "falso", expectations: func(t *testing.T, v *bool, err error) { assert.NotNil(t, err, "err should be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") assert.Nil(t, v, "result should be nil") }, }, { name: "false-error5", json: "falsea", expectations: func(t *testing.T, v *bool, err error) { assert.NotNil(t, err, "err should be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") assert.Nil(t, v, "result should be nil") }, }, { name: "false-error6", json: "f", expectations: func(t *testing.T, v *bool, err error) { assert.NotNil(t, err, "err should be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") assert.Nil(t, v, "result should be nil") }, }, { name: "false-error7", json: "a", expectations: func(t *testing.T, v *bool, err error) { assert.NotNil(t, err, "err should be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") assert.Nil(t, v, "result should be nil") }, }, { name: "null-error", json: "nall", expectations: func(t *testing.T, v *bool, err error) { assert.NotNil(t, err, "err should be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") assert.Nil(t, v, "result should be nil") }, }, { name: "null-error2", json: "nual", expectations: func(t *testing.T, v *bool, err error) { assert.NotNil(t, err, "err should be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") assert.Nil(t, v, "result should be nil") }, }, { name: "null-error3", json: "nula", expectations: func(t *testing.T, v *bool, err error) { assert.NotNil(t, err, "err should be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") assert.Nil(t, v, "result should be nil") }, }, { name: "null-error4", json: "nulle", expectations: func(t *testing.T, v *bool, err error) { assert.NotNil(t, err, "err should be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") assert.Nil(t, v, "result should be nil") }, }, { name: "null-error5", json: "n", expectations: func(t *testing.T, v *bool, err error) { assert.NotNil(t, err, "err should be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") assert.Nil(t, v, "result should be nil") }, }, { name: "null-error6", json: "a", expectations: func(t *testing.T, v *bool, err error) { assert.NotNil(t, err, "err should be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") assert.Nil(t, v, "result should be nil") }, }, { name: "null-skip", json: "{}", expectations: func(t *testing.T, v *bool, err error) { assert.NotNil(t, err, "err should not be nil") assert.IsType(t, InvalidUnmarshalError(""), err, "err should be of type InvalidUnmarshalError") assert.Nil(t, v, "result should be nil") }, }, { name: "null-skip", json: "", expectations: func(t *testing.T, v *bool, err error) { assert.Nil(t, err, "err should not be nil") assert.Nil(t, v, "result should be nil") }, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { var v = struct { b *bool }{} err := Unmarshal([]byte(testCase.json), &v.b) testCase.expectations(t, v.b, err) }) } t.Run("decoder-api-invalid-json2", func(t *testing.T) { var v = new(bool) var dec = NewDecoder(strings.NewReader(`folse`)) err := dec.BoolNull(&v) assert.NotNil(t, err, "Err must not be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") }) } func TestDecoderBoolDecoderAPI(t *testing.T) { var v bool dec := BorrowDecoder(strings.NewReader("true")) defer dec.Release() err := dec.DecodeBool(&v) assert.Nil(t, err, "Err must be nil") assert.Equal(t, true, v, "v must be equal to true") } func TestDecoderBoolPoolError(t *testing.T) { v := true dec := NewDecoder(nil) dec.Release() defer func() { err := recover() assert.NotNil(t, err, "err shouldnt be nil") assert.IsType(t, InvalidUsagePooledDecoderError(""), err, "err should be of type InvalidUsagePooledDecoderError") }() _ = dec.DecodeBool(&v) assert.True(t, false, "should not be called as decoder should have panicked") } golang-github-francoispqt-gojay-1.2.13/decode_embedded_json.go000066400000000000000000000041671365331665100244200ustar00rootroot00000000000000package gojay // EmbeddedJSON is a raw encoded JSON value. // It can be used to delay JSON decoding or precompute a JSON encoding. type EmbeddedJSON []byte func (dec *Decoder) decodeEmbeddedJSON(ej *EmbeddedJSON) error { var err error if ej == nil { return InvalidUnmarshalError("Invalid nil pointer given") } var beginOfEmbeddedJSON int for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { switch dec.data[dec.cursor] { case ' ', '\n', '\t', '\r', ',': continue // is null case 'n': beginOfEmbeddedJSON = dec.cursor dec.cursor++ err := dec.assertNull() if err != nil { return err } case 't': beginOfEmbeddedJSON = dec.cursor dec.cursor++ err := dec.assertTrue() if err != nil { return err } // is false case 'f': beginOfEmbeddedJSON = dec.cursor dec.cursor++ err := dec.assertFalse() if err != nil { return err } // is an object case '{': beginOfEmbeddedJSON = dec.cursor dec.cursor = dec.cursor + 1 dec.cursor, err = dec.skipObject() // is string case '"': beginOfEmbeddedJSON = dec.cursor dec.cursor = dec.cursor + 1 err = dec.skipString() // why no new dec.cursor in result? // is array case '[': beginOfEmbeddedJSON = dec.cursor dec.cursor = dec.cursor + 1 dec.cursor, err = dec.skipArray() case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-': beginOfEmbeddedJSON = dec.cursor dec.cursor, err = dec.skipNumber() } break } if err == nil { if dec.cursor-1 >= beginOfEmbeddedJSON { *ej = append(*ej, dec.data[beginOfEmbeddedJSON:dec.cursor]...) } dec.called |= 1 } return err } // AddEmbeddedJSON adds an EmbeddedsJSON to the value pointed by v. // It can be used to delay JSON decoding or precompute a JSON encoding. func (dec *Decoder) AddEmbeddedJSON(v *EmbeddedJSON) error { return dec.EmbeddedJSON(v) } // EmbeddedJSON adds an EmbeddedsJSON to the value pointed by v. // It can be used to delay JSON decoding or precompute a JSON encoding. func (dec *Decoder) EmbeddedJSON(v *EmbeddedJSON) error { err := dec.decodeEmbeddedJSON(v) if err != nil { return err } dec.called |= 1 return nil } golang-github-francoispqt-gojay-1.2.13/decode_embedded_json_test.go000066400000000000000000000126161365331665100254550ustar00rootroot00000000000000package gojay import ( "bytes" "strings" "testing" "github.com/stretchr/testify/assert" ) type Request struct { id string method string params EmbeddedJSON more int } func (r *Request) UnmarshalJSONObject(dec *Decoder, key string) error { switch key { case "id": return dec.AddString(&r.id) case "method": return dec.AddString(&r.method) case "params": return dec.AddEmbeddedJSON(&r.params) case "more": dec.AddInt(&r.more) } return nil } func (r *Request) NKeys() int { return 4 } func TestDecodeEmbeddedJSONUnmarshalAPI(t *testing.T) { testCases := []struct { name string json []byte expectedEmbedded string err bool }{ { name: "decode-basic-string", json: []byte(`{"id":"someid","method":"getmydata","params":"raw data", "more":123}`), expectedEmbedded: `"raw data"`, }, { name: "decode-basic-int", json: []byte(`{"id":"someid","method":"getmydata","params":12345, "more":123}`), expectedEmbedded: `12345`, }, { name: "decode-basic-int", json: []byte(`{"id":"someid","method":"getmydata","params":true, "more":123}`), expectedEmbedded: `true`, }, { name: "decode-basic-int", json: []byte(`{"id":"someid","method":"getmydata","params": false, "more":123}`), expectedEmbedded: `false`, }, { name: "decode-basic-int", json: []byte(`{"id":"someid","method":"getmydata","params":null, "more":123}`), expectedEmbedded: `null`, }, { name: "decode-basic-object", json: []byte(`{"id":"someid","method":"getmydata","params":{"example":"of raw data"}, "more":123}`), expectedEmbedded: `{"example":"of raw data"}`, }, { name: "decode-basic-object", json: []byte(`{"id":"someid","method":"getmydata","params":[1,2,3], "more":123}`), expectedEmbedded: `[1,2,3]`, }, { name: "decode-null-err", json: []byte(`{"id":"someid","method":"getmydata","params":nil, "more":123}`), expectedEmbedded: ``, err: true, }, { name: "decode-bool-false-err", json: []byte(`{"id":"someid","method":"getmydata","params":faulse, "more":123}`), expectedEmbedded: ``, err: true, }, { name: "decode-bool-true-err", json: []byte(`{"id":"someid","method":"getmydata","params":trou, "more":123}`), expectedEmbedded: ``, err: true, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { req := &Request{} err := Unmarshal(testCase.json, req) t.Log(req) t.Log(string(req.params)) if testCase.err { assert.NotNil(t, err, "err should not be nil") } else { assert.Nil(t, err, "err should be nil") } assert.Equal(t, testCase.expectedEmbedded, string(req.params), "r.params should be equal to expectedEmbeddedResult") }) } } func TestDecodeEmbeddedJSONDecodeAPI(t *testing.T) { testCases := []struct { name string json []byte expectedEmbedded string }{ { name: "decode-basic-string", json: []byte(`{"id":"someid","method":"getmydata","params":"raw data", "more":123}`), expectedEmbedded: `"raw data"`, }, { name: "decode-basic-int", json: []byte(`{"id":"someid","method":"getmydata","params":12345, "more":123}`), expectedEmbedded: `12345`, }, { name: "decode-basic-int", json: []byte(`{"id":"someid","method":"getmydata","params":true, "more":123}`), expectedEmbedded: `true`, }, { name: "decode-basic-int", json: []byte(`{"id":"someid","method":"getmydata","params": false, "more":123}`), expectedEmbedded: `false`, }, { name: "decode-basic-int", json: []byte(`{"id":"someid","method":"getmydata","params":null, "more":123}`), expectedEmbedded: `null`, }, { name: "decode-basic-object", json: []byte(`{"id":"someid","method":"getmydata","params":{"example":"of raw data"}, "more":123}`), expectedEmbedded: `{"example":"of raw data"}`, }, { name: "decode-basic-object", json: []byte(`{"id":"someid","method":"getmydata","params":[1,2,3], "more":123}`), expectedEmbedded: `[1,2,3]`, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { ej := EmbeddedJSON([]byte{}) dec := BorrowDecoder(bytes.NewReader(testCase.json)) err := dec.Decode(&ej) assert.Nil(t, err, "err should be nil") assert.Equal(t, string(testCase.json), string(ej), "r.params should be equal to expectedEmbeddedResult") }) } } func TestDecodeEmbeededJSONNil(t *testing.T) { dec := BorrowDecoder(strings.NewReader(`"bar"`)) var ej *EmbeddedJSON err := dec.decodeEmbeddedJSON(ej) assert.NotNil(t, err, `err should not be nil a nil pointer is given`) assert.IsType(t, InvalidUnmarshalError(""), err, `err should not be of type InvalidUnmarshalError`) } func TestDecodeEmbeededJSONNil2(t *testing.T) { dec := BorrowDecoder(strings.NewReader(`"bar"`)) var ej *EmbeddedJSON err := dec.AddEmbeddedJSON(ej) assert.NotNil(t, err, `err should not be nil a nil pointer is given`) assert.IsType(t, InvalidUnmarshalError(""), err, `err should not be of type InvalidUnmarshalError`) } golang-github-francoispqt-gojay-1.2.13/decode_example_test.go000066400000000000000000000050321365331665100243200ustar00rootroot00000000000000package gojay_test import ( "fmt" "log" "strings" "github.com/francoispqt/gojay" ) func ExampleUnmarshal_string() { data := []byte(`"gojay"`) var str string err := gojay.Unmarshal(data, &str) if err != nil { log.Fatal(err) } fmt.Println(str) // true } func ExampleUnmarshal_bool() { data := []byte(`true`) var b bool err := gojay.Unmarshal(data, &b) if err != nil { log.Fatal(err) } fmt.Println(b) // true } func ExampleUnmarshal_invalidType() { data := []byte(`"gojay"`) someStruct := struct{}{} err := gojay.Unmarshal(data, &someStruct) fmt.Println(err) // "Cannot unmarshal JSON to type '*struct{}'" } func ExampleDecoder_Decode_string() { var str string dec := gojay.BorrowDecoder(strings.NewReader(`"gojay"`)) err := dec.Decode(&str) dec.Release() if err != nil { log.Fatal(err) } fmt.Println(str) // "gojay" } func ExampleDecodeObjectFunc() { reader := strings.NewReader(`{ "name": "John Doe", "email": "john.doe@email.com" }`) dec := gojay.NewDecoder(reader) user := struct { name string email string }{} dec.DecodeObject(gojay.DecodeObjectFunc(func(dec *gojay.Decoder, k string) error { switch k { case "name": return dec.String(&user.name) case "email": return dec.String(&user.email) } return nil })) fmt.Printf("User\nname: %s\nemail: %s", user.name, user.email) // Output: // User // name: John Doe // email: john.doe@email.com } func ExampleDecodeArrayFunc() { reader := strings.NewReader(`[ "foo", "bar" ]`) dec := gojay.NewDecoder(reader) strSlice := make([]string, 0) err := dec.DecodeArray(gojay.DecodeArrayFunc(func(dec *gojay.Decoder) error { var str string if err := dec.AddString(&str); err != nil { return err } strSlice = append(strSlice, str) return nil })) if err != nil { log.Fatal(err) } fmt.Print(strSlice) // Output: // [foo bar] } func ExampleNewDecoder() { reader := strings.NewReader(`"gojay"`) dec := gojay.NewDecoder(reader) var str string err := dec.DecodeString(&str) if err != nil { log.Fatal(err) } fmt.Println(str) // Output: // gojay } func ExampleBorrowDecoder() { reader := strings.NewReader(`"gojay"`) dec := gojay.BorrowDecoder(reader) defer dec.Release() var str string err := dec.DecodeString(&str) if err != nil { log.Fatal(err) } fmt.Println(str) // Output: // gojay } func ExampleDecoder_DecodeBool() { reader := strings.NewReader(`true`) dec := gojay.NewDecoder(reader) var b bool err := dec.DecodeBool(&b) if err != nil { log.Fatal(err) } fmt.Println(b) // Output: // true } golang-github-francoispqt-gojay-1.2.13/decode_interface.go000066400000000000000000000055351365331665100235760ustar00rootroot00000000000000package gojay // TODO @afiune for now we are using the standard json unmarshaling but in // the future it would be great to implement one here inside this repo import "encoding/json" // DecodeInterface reads the next JSON-encoded value from the decoder's input (io.Reader) and stores it in the value pointed to by i. // // i must be an interface poiter func (dec *Decoder) DecodeInterface(i *interface{}) error { if dec.isPooled == 1 { panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) } err := dec.decodeInterface(i) return err } func (dec *Decoder) decodeInterface(i *interface{}) error { start, end, err := dec.getObject() if err != nil { dec.cursor = start return err } // if start & end are equal the object is a null, don't unmarshal if start == end { return nil } object := dec.data[start:end] if err = json.Unmarshal(object, i); err != nil { return err } dec.cursor = end return nil } // @afiune Maybe return the type as well? func (dec *Decoder) getObject() (start int, end int, err error) { // start cursor for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { switch dec.data[dec.cursor] { case ' ', '\n', '\t', '\r', ',': continue // is null case 'n': dec.cursor++ err = dec.assertNull() if err != nil { return } // Set start & end to the same cursor to indicate the object // is a null and should not be unmarshal start = dec.cursor end = dec.cursor return case 't': start = dec.cursor dec.cursor++ err = dec.assertTrue() if err != nil { return } end = dec.cursor dec.cursor++ return // is false case 'f': start = dec.cursor dec.cursor++ err = dec.assertFalse() if err != nil { return } end = dec.cursor dec.cursor++ return // is an object case '{': start = dec.cursor dec.cursor++ end, err = dec.skipObject() dec.cursor = end return // is string case '"': start = dec.cursor dec.cursor++ start, end, err = dec.getString() start-- dec.cursor = end return // is array case '[': start = dec.cursor dec.cursor++ end, err = dec.skipArray() dec.cursor = end return case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-': start = dec.cursor end, err = dec.skipNumber() dec.cursor = end return default: err = dec.raiseInvalidJSONErr(dec.cursor) return } } err = dec.raiseInvalidJSONErr(dec.cursor) return } // Add Values functions // AddInterface decodes the JSON value within an object or an array to a interface{}. func (dec *Decoder) AddInterface(v *interface{}) error { return dec.Interface(v) } // Interface decodes the JSON value within an object or an array to an interface{}. func (dec *Decoder) Interface(value *interface{}) error { err := dec.decodeInterface(value) if err != nil { return err } dec.called |= 1 return nil } golang-github-francoispqt-gojay-1.2.13/decode_interface_test.go000066400000000000000000000316141365331665100246320ustar00rootroot00000000000000package gojay import ( "encoding/json" "strings" "testing" "github.com/stretchr/testify/assert" ) func TestDecodeInterfaceBasic(t *testing.T) { testCases := []struct { name string json string expectedResult interface{} err bool errType interface{} skipCheckResult bool }{ { name: "array", json: `[1,2,3]`, expectedResult: []interface{}([]interface{}{float64(1), float64(2), float64(3)}), err: false, }, { name: "object", json: `{"testStr": "hello world!"}`, expectedResult: map[string]interface{}(map[string]interface{}{"testStr": "hello world!"}), err: false, }, { name: "string", json: `"hola amigos!"`, expectedResult: interface{}("hola amigos!"), err: false, }, { name: "bool-true", json: `true`, expectedResult: interface{}(true), err: false, }, { name: "bool-false", json: `false`, expectedResult: interface{}(false), err: false, }, { name: "null", json: `null`, expectedResult: interface{}(nil), err: false, }, { name: "number", json: `1234`, expectedResult: interface{}(float64(1234)), err: false, }, { name: "array-error", json: `["h""o","l","a"]`, err: true, errType: &json.SyntaxError{}, skipCheckResult: true, }, { name: "object-error", json: `{"testStr" "hello world!"}`, err: true, errType: &json.SyntaxError{}, skipCheckResult: true, }, { name: "string-error", json: `"hola amigos!`, err: true, errType: InvalidJSONError(""), skipCheckResult: true, }, { name: "bool-true-error", json: `truee`, err: true, errType: InvalidJSONError(""), skipCheckResult: true, }, { name: "bool-false-error", json: `fase`, expectedResult: interface{}(false), err: true, errType: InvalidJSONError(""), skipCheckResult: true, }, { name: "null-error", json: `nulllll`, err: true, errType: InvalidJSONError(""), skipCheckResult: true, }, { name: "number-error", json: `1234"`, err: true, errType: InvalidJSONError(""), skipCheckResult: true, }, { name: "unknown-error", json: `?`, err: true, errType: InvalidJSONError(""), skipCheckResult: true, }, { name: "empty-json-error", json: ``, err: true, errType: InvalidJSONError(""), skipCheckResult: true, }, } for _, testCase := range testCases { t.Run("DecodeInterface()"+testCase.name, func(t *testing.T) { var i interface{} dec := BorrowDecoder(strings.NewReader(testCase.json)) defer dec.Release() err := dec.DecodeInterface(&i) if testCase.err { t.Log(err) assert.NotNil(t, err, "err should not be nil") if testCase.errType != nil { assert.IsType(t, testCase.errType, err, "err should be of the given type") } return } assert.Nil(t, err, "err should be nil") if !testCase.skipCheckResult { assert.Equal(t, testCase.expectedResult, i, "value at given index should be the same as expected results") } }) } for _, testCase := range testCases { t.Run("Decode()"+testCase.name, func(t *testing.T) { var i interface{} dec := BorrowDecoder(strings.NewReader(testCase.json)) defer dec.Release() err := dec.Decode(&i) if testCase.err { t.Log(err) assert.NotNil(t, err, "err should not be nil") if testCase.errType != nil { assert.IsType(t, testCase.errType, err, "err should be of the given type") } return } assert.Nil(t, err, "err should be nil") if !testCase.skipCheckResult { assert.Equal(t, testCase.expectedResult, i, "value at given index should be the same as expected results") } }) } } func TestDecodeInterfaceAsInterface(t *testing.T) { testCases := []struct { name string json string expectedResult interface{} err bool errType interface{} skipCheckResult bool }{ { name: "basic-array", json: `{ "testStr": "hola", "testInterface": ["h","o","l","a"] }`, expectedResult: map[string]interface{}( map[string]interface{}{ "testStr": "hola", "testInterface": []interface{}{"h", "o", "l", "a"}, }), err: false, }, { name: "basic-string", json: `{ "testInterface": "漢字" }`, expectedResult: map[string]interface{}( map[string]interface{}{ "testInterface": "漢字", }), err: false, }, { name: "basic-error", json: `{ "testInterface": ["a""d","i","o","s"] }`, err: true, errType: &json.SyntaxError{}, skipCheckResult: true, }, { name: "basic-interface", json: `{ "testInterface": { "string": "prost" } }`, expectedResult: map[string]interface{}( map[string]interface{}{ "testInterface": map[string]interface{}{"string": "prost"}, }), err: false, }, { name: "complex-interface", json: `{ "testInterface": { "number": 1988, "string": "prost", "array": ["h","o","l","a"], "object": { "k": "v", "a": [1,2,3] }, "array-of-objects": [ {"k": "v"}, {"a": "b"} ] } }`, expectedResult: map[string]interface{}( map[string]interface{}{ "testInterface": map[string]interface{}{ "array-of-objects": []interface{}{ map[string]interface{}{"k": "v"}, map[string]interface{}{"a": "b"}, }, "number": float64(1988), "string": "prost", "array": []interface{}{"h", "o", "l", "a"}, "object": map[string]interface{}{ "k": "v", "a": []interface{}{float64(1), float64(2), float64(3)}, }, }, }), err: false, }, } for _, testCase := range testCases { t.Run("Decode()"+testCase.name, func(t *testing.T) { var s interface{} dec := BorrowDecoder(strings.NewReader(testCase.json)) defer dec.Release() err := dec.Decode(&s) if testCase.err { t.Log(err) assert.NotNil(t, err, "err should not be nil") if testCase.errType != nil { assert.IsType(t, testCase.errType, err, "err should be of the given type") } return } assert.Nil(t, err, "err should be nil") if !testCase.skipCheckResult { assert.Equal(t, testCase.expectedResult, s, "value at given index should be the same as expected results") } }) } for _, testCase := range testCases { t.Run("DecodeInterface()"+testCase.name, func(t *testing.T) { var s interface{} dec := BorrowDecoder(strings.NewReader(testCase.json)) defer dec.Release() err := dec.DecodeInterface(&s) if testCase.err { t.Log(err) assert.NotNil(t, err, "err should not be nil") if testCase.errType != nil { assert.IsType(t, testCase.errType, err, "err should be of the given type") } return } assert.Nil(t, err, "err should be nil") if !testCase.skipCheckResult { assert.Equal(t, testCase.expectedResult, s, "value at given index should be the same as expected results") } }) } } func TestDecodeAsTestObject(t *testing.T) { testCases := []struct { name string json string expectedResult testObject err bool errType interface{} skipCheckResult bool }{ { name: "basic-array", json: `{ "testStr": "hola", "testInterface": ["h","o","l","a"] }`, expectedResult: testObject{ testStr: "hola", testInterface: []interface{}([]interface{}{"h", "o", "l", "a"}), }, err: false, }, { name: "basic-string", json: `{ "testInterface": "漢字" }`, expectedResult: testObject{ testInterface: interface{}("漢字"), }, err: false, }, { name: "basic-error", json: `{ "testInterface": ["a""d","i","o","s"] }`, err: true, errType: &json.SyntaxError{}, skipCheckResult: true, }, { name: "mull-interface", json: `{ "testInterface": null, "testStr": "adios" }`, expectedResult: testObject{ testInterface: interface{}(nil), testStr: "adios", }, err: false, }, { name: "basic-interface", json: `{ "testInterface": { "string": "prost" }, }`, expectedResult: testObject{ testInterface: map[string]interface{}{"string": "prost"}, }, err: false, }, { name: "complex-interface", json: `{ "testInterface": { "number": 1988, "string": "prost", "array": ["h","o","l","a"], "object": { "k": "v", "a": [1,2,3] }, "array-of-objects": [ {"k": "v"}, {"a": "b"} ] }, }`, expectedResult: testObject{ testInterface: map[string]interface{}{ "array-of-objects": []interface{}{ map[string]interface{}{"k": "v"}, map[string]interface{}{"a": "b"}, }, "number": float64(1988), "string": "prost", "array": []interface{}{"h", "o", "l", "a"}, "object": map[string]interface{}{ "k": "v", "a": []interface{}{float64(1), float64(2), float64(3)}, }, }, }, err: false, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { s := testObject{} dec := BorrowDecoder(strings.NewReader(testCase.json)) defer dec.Release() err := dec.Decode(&s) if testCase.err { t.Log(err) assert.NotNil(t, err, "err should not be nil") if testCase.errType != nil { assert.IsType(t, testCase.errType, err, "err should be of the given type") } return } assert.Nil(t, err, "err should be nil") if !testCase.skipCheckResult { assert.Equal(t, testCase.expectedResult, s, "value at given index should be the same as expected results") } }) } } func TestUnmarshalInterface(t *testing.T) { json := []byte(`{ "testInterface": { "number": 1988, "null": null, "string": "prost", "array": ["h","o","l","a"], "object": { "k": "v", "a": [1,2,3] }, "array-of-objects": [ {"k": "v"}, {"a": "b"} ] } }`) v := &testObject{} err := Unmarshal(json, v) assert.Nil(t, err, "Err must be nil") expectedInterface := map[string]interface{}{ "array-of-objects": []interface{}{ map[string]interface{}{"k": "v"}, map[string]interface{}{"a": "b"}, }, "number": float64(1988), "string": "prost", "null": interface{}(nil), "array": []interface{}{"h", "o", "l", "a"}, "object": map[string]interface{}{ "k": "v", "a": []interface{}{float64(1), float64(2), float64(3)}, }, } assert.Equal(t, expectedInterface, v.testInterface, "v.testInterface must be equal to the expected one") } func TestUnmarshalInterfaceError(t *testing.T) { testCases := []struct { name string json []byte }{ { name: "basic", json: []byte(`{"testInterface": {"number": 1bc4}}`), }, { name: "syntax", json: []byte(`{ "testInterface": { "array?": [1,"a", ?] } }`), }, { name: "complex", json: []byte(`{ "testInterface": { "number": 1988, "string": "prost", "array": ["h""o","l","a"], "object": { "k": "v", "a": [1,2,3] }, "array-of-objects": [ {"k": "v"}, {"a": "b"} ] } }`), }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { v := &testObject{} err := Unmarshal(testCase.json, v) assert.NotNil(t, err, "Err must be not nil") t.Log(err) assert.IsType(t, &json.SyntaxError{}, err, "err should be a json.SyntaxError{}") }) } } func TestDecodeInterfacePoolError(t *testing.T) { result := interface{}(1) dec := NewDecoder(nil) dec.Release() defer func() { err := recover() assert.NotNil(t, err, "err shouldnt be nil") assert.IsType(t, InvalidUsagePooledDecoderError(""), err, "err should be of type InvalidUsagePooledDecoderError") }() _ = dec.DecodeInterface(&result) assert.True(t, false, "should not be called as decoder should have panicked") } func TestDecodeNull(t *testing.T) { var i interface{} dec := BorrowDecoder(strings.NewReader("null")) defer dec.Release() err := dec.DecodeInterface(&i) assert.Nil(t, err, "err should be nil") assert.Equal(t, interface{}(nil), i, "value at given index should be the same as expected results") } golang-github-francoispqt-gojay-1.2.13/decode_number.go000066400000000000000000000051221365331665100231160ustar00rootroot00000000000000package gojay import ( "math" ) var digits []int8 const maxInt64toMultiply = math.MaxInt64 / 10 const maxInt32toMultiply = math.MaxInt32 / 10 const maxInt16toMultiply = math.MaxInt16 / 10 const maxInt8toMultiply = math.MaxInt8 / 10 const maxUint8toMultiply = math.MaxUint8 / 10 const maxUint16toMultiply = math.MaxUint16 / 10 const maxUint32toMultiply = math.MaxUint32 / 10 const maxUint64toMultiply = math.MaxUint64 / 10 const maxUint32Length = 10 const maxUint64Length = 20 const maxUint16Length = 5 const maxUint8Length = 3 const maxInt32Length = 10 const maxInt64Length = 19 const maxInt16Length = 5 const maxInt8Length = 3 const invalidNumber = int8(-1) var pow10uint64 = [21]uint64{ 0, 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, 10000000000, 100000000000, 1000000000000, 10000000000000, 100000000000000, 1000000000000000, 10000000000000000, 100000000000000000, 1000000000000000000, 10000000000000000000, } var skipNumberEndCursorIncrement [256]int func init() { digits = make([]int8, 256) for i := 0; i < len(digits); i++ { digits[i] = invalidNumber } for i := int8('0'); i <= int8('9'); i++ { digits[i] = i - int8('0') } for i := 0; i < 256; i++ { switch i { case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.', 'e', 'E', '+', '-': skipNumberEndCursorIncrement[i] = 1 } } } func (dec *Decoder) skipNumber() (int, error) { end := dec.cursor + 1 // look for following numbers for j := dec.cursor + 1; j < dec.length || dec.read(); j++ { end += skipNumberEndCursorIncrement[dec.data[j]] switch dec.data[j] { case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.', 'e', 'E', '+', '-', ' ', '\n', '\t', '\r': continue case ',', '}', ']': return end, nil default: // invalid json we expect numbers, dot (single one), comma, or spaces return end, dec.raiseInvalidJSONErr(dec.cursor) } } return end, nil } func (dec *Decoder) getExponent() (int64, error) { start := dec.cursor end := dec.cursor for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { switch dec.data[dec.cursor] { // is positive case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': end = dec.cursor + 1 case '-': dec.cursor++ exp, err := dec.getExponent() return -exp, err case '+': dec.cursor++ return dec.getExponent() default: // if nothing return 0 // could raise error if start == end { return 0, dec.raiseInvalidJSONErr(dec.cursor) } return dec.atoi64(start, end-1), nil } } if start == end { return 0, dec.raiseInvalidJSONErr(dec.cursor) } return dec.atoi64(start, end-1), nil } golang-github-francoispqt-gojay-1.2.13/decode_number_float.go000066400000000000000000000362321365331665100243110ustar00rootroot00000000000000package gojay // DecodeFloat64 reads the next JSON-encoded value from the decoder's input (io.Reader) and stores it in the float64 pointed to by v. // // See the documentation for Unmarshal for details about the conversion of JSON into a Go value. func (dec *Decoder) DecodeFloat64(v *float64) error { if dec.isPooled == 1 { panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) } return dec.decodeFloat64(v) } func (dec *Decoder) decodeFloat64(v *float64) error { for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { switch c := dec.data[dec.cursor]; c { case ' ', '\n', '\t', '\r', ',': continue case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': val, err := dec.getFloat() if err != nil { return err } *v = val return nil case '-': dec.cursor = dec.cursor + 1 val, err := dec.getFloatNegative() if err != nil { return err } *v = -val return nil case 'n': dec.cursor++ err := dec.assertNull() if err != nil { return err } return nil default: dec.err = dec.makeInvalidUnmarshalErr(v) err := dec.skipData() if err != nil { return err } return nil } } return dec.raiseInvalidJSONErr(dec.cursor) } func (dec *Decoder) decodeFloat64Null(v **float64) error { for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { switch c := dec.data[dec.cursor]; c { case ' ', '\n', '\t', '\r', ',': continue case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': val, err := dec.getFloat() if err != nil { return err } if *v == nil { *v = new(float64) } **v = val return nil case '-': dec.cursor = dec.cursor + 1 val, err := dec.getFloatNegative() if err != nil { return err } if *v == nil { *v = new(float64) } **v = -val return nil case 'n': dec.cursor++ err := dec.assertNull() if err != nil { return err } return nil default: dec.err = dec.makeInvalidUnmarshalErr(v) err := dec.skipData() if err != nil { return err } return nil } } return dec.raiseInvalidJSONErr(dec.cursor) } func (dec *Decoder) getFloatNegative() (float64, error) { // look for following numbers for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { switch dec.data[dec.cursor] { case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': return dec.getFloat() default: return 0, dec.raiseInvalidJSONErr(dec.cursor) } } return 0, dec.raiseInvalidJSONErr(dec.cursor) } func (dec *Decoder) getFloat() (float64, error) { var end = dec.cursor var start = dec.cursor // look for following numbers for j := dec.cursor + 1; j < dec.length || dec.read(); j++ { switch dec.data[j] { case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': end = j continue case '.': // we get part before decimal as integer beforeDecimal := dec.atoi64(start, end) // then we get part after decimal as integer start = j + 1 // get number after the decimal point for i := j + 1; i < dec.length || dec.read(); i++ { c := dec.data[i] if isDigit(c) { end = i // multiply the before decimal point portion by 10 using bitwise // make sure it doesn't overflow if end-start < 18 { beforeDecimal = (beforeDecimal << 3) + (beforeDecimal << 1) } continue } else if (c == 'e' || c == 'E') && j < i-1 { // we have an exponent, convert first the value we got before the exponent var afterDecimal int64 expI := end - start + 2 // if exp is too long, it means number is too long, just truncate the number if expI >= len(pow10uint64) || expI < 0 { expI = len(pow10uint64) - 2 afterDecimal = dec.atoi64(start, start+expI-2) } else { // then we add both integers // then we divide the number by the power found afterDecimal = dec.atoi64(start, end) } dec.cursor = i + 1 pow := pow10uint64[expI] floatVal := float64(beforeDecimal+afterDecimal) / float64(pow) exp, err := dec.getExponent() if err != nil { return 0, err } pExp := (exp + (exp >> 31)) ^ (exp >> 31) + 1 // absolute exponent if pExp >= int64(len(pow10uint64)) || pExp < 0 { return 0, dec.raiseInvalidJSONErr(dec.cursor) } // if exponent is negative if exp < 0 { return float64(floatVal) * (1 / float64(pow10uint64[pExp])), nil } return float64(floatVal) * float64(pow10uint64[pExp]), nil } dec.cursor = i break } if end >= dec.length || end < start { return 0, dec.raiseInvalidJSONErr(dec.cursor) } var afterDecimal int64 expI := end - start + 2 // if exp is too long, it means number is too long, just truncate the number if expI >= len(pow10uint64) || expI < 0 { expI = 19 afterDecimal = dec.atoi64(start, start+expI-2) } else { afterDecimal = dec.atoi64(start, end) } pow := pow10uint64[expI] // then we add both integers // then we divide the number by the power found return float64(beforeDecimal+afterDecimal) / float64(pow), nil case 'e', 'E': dec.cursor = j + 1 // we get part before decimal as integer beforeDecimal := uint64(dec.atoi64(start, end)) // get exponent exp, err := dec.getExponent() if err != nil { return 0, err } pExp := (exp + (exp >> 31)) ^ (exp >> 31) + 1 // abs if pExp >= int64(len(pow10uint64)) || pExp < 0 { return 0, dec.raiseInvalidJSONErr(dec.cursor) } // if exponent is negative if exp < 0 { return float64(beforeDecimal) * (1 / float64(pow10uint64[pExp])), nil } return float64(beforeDecimal) * float64(pow10uint64[pExp]), nil case ' ', '\n', '\t', '\r', ',', '}', ']': // does not have decimal dec.cursor = j return float64(dec.atoi64(start, end)), nil } // invalid json we expect numbers, dot (single one), comma, or spaces return 0, dec.raiseInvalidJSONErr(dec.cursor) } return float64(dec.atoi64(start, end)), nil } // DecodeFloat32 reads the next JSON-encoded value from the decoder's input (io.Reader) and stores it in the float32 pointed to by v. // // See the documentation for Unmarshal for details about the conversion of JSON into a Go value. func (dec *Decoder) DecodeFloat32(v *float32) error { if dec.isPooled == 1 { panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) } return dec.decodeFloat32(v) } func (dec *Decoder) decodeFloat32(v *float32) error { for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { switch c := dec.data[dec.cursor]; c { case ' ', '\n', '\t', '\r', ',': continue case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': val, err := dec.getFloat32() if err != nil { return err } *v = val return nil case '-': dec.cursor = dec.cursor + 1 val, err := dec.getFloat32Negative() if err != nil { return err } *v = -val return nil case 'n': dec.cursor++ err := dec.assertNull() if err != nil { return err } return nil default: dec.err = dec.makeInvalidUnmarshalErr(v) err := dec.skipData() if err != nil { return err } return nil } } return dec.raiseInvalidJSONErr(dec.cursor) } func (dec *Decoder) decodeFloat32Null(v **float32) error { for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { switch c := dec.data[dec.cursor]; c { case ' ', '\n', '\t', '\r', ',': continue case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': val, err := dec.getFloat32() if err != nil { return err } if *v == nil { *v = new(float32) } **v = val return nil case '-': dec.cursor = dec.cursor + 1 val, err := dec.getFloat32Negative() if err != nil { return err } if *v == nil { *v = new(float32) } **v = -val return nil case 'n': dec.cursor++ err := dec.assertNull() if err != nil { return err } return nil default: dec.err = dec.makeInvalidUnmarshalErr(v) err := dec.skipData() if err != nil { return err } return nil } } return dec.raiseInvalidJSONErr(dec.cursor) } func (dec *Decoder) getFloat32Negative() (float32, error) { // look for following numbers for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { switch dec.data[dec.cursor] { case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': return dec.getFloat32() default: return 0, dec.raiseInvalidJSONErr(dec.cursor) } } return 0, dec.raiseInvalidJSONErr(dec.cursor) } func (dec *Decoder) getFloat32() (float32, error) { var end = dec.cursor var start = dec.cursor // look for following numbers for j := dec.cursor + 1; j < dec.length || dec.read(); j++ { switch dec.data[j] { case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': end = j continue case '.': // we get part before decimal as integer beforeDecimal := dec.atoi64(start, end) // then we get part after decimal as integer start = j + 1 // get number after the decimal point // multiple the before decimal point portion by 10 using bitwise for i := j + 1; i < dec.length || dec.read(); i++ { c := dec.data[i] if isDigit(c) { end = i // multiply the before decimal point portion by 10 using bitwise // make sure it desn't overflow if end-start < 9 { beforeDecimal = (beforeDecimal << 3) + (beforeDecimal << 1) } continue } else if (c == 'e' || c == 'E') && j < i-1 { // we get the number before decimal var afterDecimal int64 expI := end - start + 2 // if exp is too long, it means number is too long, just truncate the number if expI >= 12 || expI < 0 { expI = 10 afterDecimal = dec.atoi64(start, start+expI-2) } else { afterDecimal = dec.atoi64(start, end) } dec.cursor = i + 1 pow := pow10uint64[expI] // then we add both integers // then we divide the number by the power found floatVal := float32(beforeDecimal+afterDecimal) / float32(pow) exp, err := dec.getExponent() if err != nil { return 0, err } pExp := (exp + (exp >> 31)) ^ (exp >> 31) + 1 // abs if pExp >= int64(len(pow10uint64)) || pExp < 0 { return 0, dec.raiseInvalidJSONErr(dec.cursor) } // if exponent is negative if exp < 0 { return float32(floatVal) * (1 / float32(pow10uint64[pExp])), nil } return float32(floatVal) * float32(pow10uint64[pExp]), nil } dec.cursor = i break } if end >= dec.length || end < start { return 0, dec.raiseInvalidJSONErr(dec.cursor) } // then we add both integers // then we divide the number by the power found var afterDecimal int64 expI := end - start + 2 // if exp is too long, it means number is too long, just truncate the number if expI >= 12 || expI < 0 { expI = 10 afterDecimal = dec.atoi64(start, start+expI-2) } else { // then we add both integers // then we divide the number by the power found afterDecimal = dec.atoi64(start, end) } pow := pow10uint64[expI] return float32(beforeDecimal+afterDecimal) / float32(pow), nil case 'e', 'E': dec.cursor = j + 1 // we get part before decimal as integer beforeDecimal := dec.atoi64(start, end) // get exponent exp, err := dec.getExponent() if err != nil { return 0, err } pExp := (exp + (exp >> 31)) ^ (exp >> 31) + 1 if pExp >= int64(len(pow10uint64)) || pExp < 0 { return 0, dec.raiseInvalidJSONErr(dec.cursor) } // if exponent is negative if exp < 0 { return float32(beforeDecimal) * (1 / float32(pow10uint64[pExp])), nil } return float32(beforeDecimal) * float32(pow10uint64[pExp]), nil case ' ', '\n', '\t', '\r', ',', '}', ']': // does not have decimal dec.cursor = j return float32(dec.atoi64(start, end)), nil } // invalid json we expect numbers, dot (single one), comma, or spaces return 0, dec.raiseInvalidJSONErr(dec.cursor) } return float32(dec.atoi64(start, end)), nil } // Add Values functions // AddFloat decodes the JSON value within an object or an array to a *float64. // If next key value overflows float64, an InvalidUnmarshalError error will be returned. func (dec *Decoder) AddFloat(v *float64) error { return dec.Float64(v) } // AddFloatNull decodes the JSON value within an object or an array to a *float64. // If next key value overflows float64, an InvalidUnmarshalError error will be returned. // If a `null` is encountered, gojay does not change the value of the pointer. func (dec *Decoder) AddFloatNull(v **float64) error { return dec.Float64Null(v) } // AddFloat64 decodes the JSON value within an object or an array to a *float64. // If next key value overflows float64, an InvalidUnmarshalError error will be returned. func (dec *Decoder) AddFloat64(v *float64) error { return dec.Float64(v) } // AddFloat64Null decodes the JSON value within an object or an array to a *float64. // If next key value overflows float64, an InvalidUnmarshalError error will be returned. // If a `null` is encountered, gojay does not change the value of the pointer. func (dec *Decoder) AddFloat64Null(v **float64) error { return dec.Float64Null(v) } // AddFloat32 decodes the JSON value within an object or an array to a *float64. // If next key value overflows float64, an InvalidUnmarshalError error will be returned. func (dec *Decoder) AddFloat32(v *float32) error { return dec.Float32(v) } // AddFloat32Null decodes the JSON value within an object or an array to a *float64. // If next key value overflows float64, an InvalidUnmarshalError error will be returned. // If a `null` is encountered, gojay does not change the value of the pointer. func (dec *Decoder) AddFloat32Null(v **float32) error { return dec.Float32Null(v) } // Float decodes the JSON value within an object or an array to a *float64. // If next key value overflows float64, an InvalidUnmarshalError error will be returned. func (dec *Decoder) Float(v *float64) error { return dec.Float64(v) } // FloatNull decodes the JSON value within an object or an array to a *float64. // If next key value overflows float64, an InvalidUnmarshalError error will be returned. func (dec *Decoder) FloatNull(v **float64) error { return dec.Float64Null(v) } // Float64 decodes the JSON value within an object or an array to a *float64. // If next key value overflows float64, an InvalidUnmarshalError error will be returned. func (dec *Decoder) Float64(v *float64) error { err := dec.decodeFloat64(v) if err != nil { return err } dec.called |= 1 return nil } // Float64Null decodes the JSON value within an object or an array to a *float64. // If next key value overflows float64, an InvalidUnmarshalError error will be returned. func (dec *Decoder) Float64Null(v **float64) error { err := dec.decodeFloat64Null(v) if err != nil { return err } dec.called |= 1 return nil } // Float32 decodes the JSON value within an object or an array to a *float64. // If next key value overflows float64, an InvalidUnmarshalError error will be returned. func (dec *Decoder) Float32(v *float32) error { err := dec.decodeFloat32(v) if err != nil { return err } dec.called |= 1 return nil } // Float32Null decodes the JSON value within an object or an array to a *float64. // If next key value overflows float64, an InvalidUnmarshalError error will be returned. func (dec *Decoder) Float32Null(v **float32) error { err := dec.decodeFloat32Null(v) if err != nil { return err } dec.called |= 1 return nil } golang-github-francoispqt-gojay-1.2.13/decode_number_float_test.go000066400000000000000000000752701365331665100253550ustar00rootroot00000000000000package gojay import ( "fmt" "math" "reflect" "strings" "testing" "github.com/stretchr/testify/assert" ) func TestDecoderFloat64(t *testing.T) { testCases := []struct { name string json string expectedResult float64 skipResult bool err bool errType interface{} }{ { name: "basic-float", json: "1.1", expectedResult: 1.1, }, { name: "basic-exponent-positive-positive-exp", json: "1e2", expectedResult: 100, }, { name: "basic-exponent-positive-positive-exp2", json: "5e+06", expectedResult: 5000000, }, { name: "basic-exponent-positive-positive-exp3", json: "3e+3", expectedResult: 3000, }, { name: "basic-null", json: "null", expectedResult: 0, }, { name: "basic-null-err", json: "nxll", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "basic-negative-err", json: "-", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "basic-negative-err", json: "-q", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "basic-null-err", json: "trua", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "basic-err1", json: "0.", expectedResult: 0, err: true, }, { name: "basic-err2", json: "-1.", expectedResult: 0, err: true, }, { name: "exponent-err-", json: "0.1e", expectedResult: 0, err: true, }, { name: "exp-err", json: "0e-20", expectedResult: 0, err: true, }, { name: "exp-err3", json: "-9e-60", expectedResult: 0, err: true, }, { name: "exp-err4", json: "0.e-2", skipResult: true, err: true, }, { name: "exp-err5", json: "-5.E-2", skipResult: true, err: true, }, { name: "basic-exponent-positive-positive-exp4", json: "8e+005", expectedResult: 800000, }, { name: "basic-exponent-positive-negative-exp", json: "1e-2", expectedResult: 0.01, }, { name: "basic-exponent-positive-negative-exp2", json: "5e-6", expectedResult: 0.000005, }, { name: "basic-exponent-positive-negative-exp3", json: "3e-3", expectedResult: 0.003, }, { name: "basic-exponent-positive-negative-exp4", json: "8e-005", expectedResult: 0.00008, }, { name: "basic-exponent-negative-positive-exp", json: "-1e2", expectedResult: -100, }, { name: "basic-exponent-negative-positive-exp2", json: "-5e+06", expectedResult: -5000000, }, { name: "basic-exponent-negative-positive-exp3", json: "-3e03", expectedResult: -3000, }, { name: "basic-float2", json: "877 ", expectedResult: 877, }, { name: "basic-exponent-negative-positive-exp4", json: "-8e+005", expectedResult: -800000, }, { name: "basic-exponent-negative-positive-exp4", json: "-8.2e-005", expectedResult: -0.000082, }, { name: "basic-float", json: "2.4595", expectedResult: 2.4595, }, { name: "basic-float2", json: "877", expectedResult: 877, }, { name: "basic-float2", json: "-7.8876", expectedResult: -7.8876, }, { name: "basic-float", json: "2.4595e1", expectedResult: 24.595, }, { name: "basic-float2", json: "-7.8876e002", expectedResult: -788.76, }, { name: "basic-float3", json: "-0.1234", expectedResult: -0.1234, }, { name: "basic-exp-too-big", json: "1e10000000000 ", expectedResult: 0, err: true, }, { name: "basic-exp-too-big", json: "1.002e10000000000 ", expectedResult: 0, err: true, }, { name: "basic-exp-too-big", json: "0e9223372036000000000 ", expectedResult: 0, err: true, }, { name: "big float", json: "1.00232492420002423545849009", expectedResult: 1.002325, }, { name: "big float", json: "5620.1400000000003", expectedResult: 5620.14, }, { name: "basic-exp-too-big", json: "1.00232492420002423545849009e10000000000 ", expectedResult: 0, err: true, }, { name: "error", json: "83zez4", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "exponent-err", json: "0.1e", expectedResult: 0, err: true, }, { name: "exponent-err", json: "0e", expectedResult: 0, err: true, }, { name: "error", json: "-83zez4", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "invalid-type", json: `"string"`, expectedResult: 0, err: true, errType: InvalidUnmarshalError(""), }, { name: "big float", json: "5620.1400000000003", expectedResult: 5620.1400000000003, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { json := []byte(testCase.json) var v float64 err := Unmarshal(json, &v) if testCase.err { assert.NotNil(t, err, "Err must not be nil") if testCase.errType != nil { assert.IsType( t, testCase.errType, err, fmt.Sprintf("err should be of type %s", reflect.TypeOf(err).String()), ) } } else { assert.Nil(t, err, "Err must be nil") } if !testCase.skipResult { assert.Equal(t, math.Round(testCase.expectedResult*1000000), math.Round(v*1000000), fmt.Sprintf("v must be equal to %f", testCase.expectedResult)) } }) } t.Run("pool-error", func(t *testing.T) { result := float64(1) dec := NewDecoder(nil) dec.Release() defer func() { err := recover() assert.NotNil(t, err, "err shouldnt be nil") assert.IsType(t, InvalidUsagePooledDecoderError(""), err, "err should be of type InvalidUsagePooledDecoderError") }() _ = dec.DecodeFloat64(&result) assert.True(t, false, "should not be called as decoder should have panicked") }) t.Run("decoder-api", func(t *testing.T) { var v float64 dec := NewDecoder(strings.NewReader(`1.25`)) defer dec.Release() err := dec.DecodeFloat64(&v) assert.Nil(t, err, "Err must be nil") assert.Equal(t, 1.25, v, "v must be equal to 1.25") }) t.Run("decoder-api2", func(t *testing.T) { var v float64 dec := NewDecoder(strings.NewReader(`1.25`)) defer dec.Release() err := dec.DecodeFloat64(&v) assert.Nil(t, err, "Err must be nil") assert.Equal(t, 1.25, v, "v must be equal to 1.25") }) t.Run("decoder-api-json-error", func(t *testing.T) { var v float64 dec := NewDecoder(strings.NewReader(``)) defer dec.Release() err := dec.DecodeFloat64(&v) assert.NotNil(t, err, "Err must not be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") }) } func TestDecoderFloat64Null(t *testing.T) { testCases := []struct { name string json string expectedResult float64 resultIsNil bool err bool errType interface{} }{ { name: "basic-float", json: "1.1", expectedResult: 1.1, }, { name: "basic-exponent-positive-positive-exp", json: " 1e2", expectedResult: 100, }, { name: "basic-exponent-positive-positive-exp2", json: "5e+06", expectedResult: 5000000, }, { name: "basic-exponent-positive-positive-exp3", json: "3e+3", expectedResult: 3000, }, { name: "basic-null", json: "null", expectedResult: 0, resultIsNil: true, }, { name: "basic-null-err", json: "nxll", expectedResult: 0, err: true, errType: InvalidJSONError(""), resultIsNil: true, }, { name: "basic-negative-err", json: "-", expectedResult: 0, err: true, errType: InvalidJSONError(""), resultIsNil: true, }, { name: "basic-negative-err", json: "-q", expectedResult: 0, err: true, errType: InvalidJSONError(""), resultIsNil: true, }, { name: "basic-null-err", json: "trua", expectedResult: 0, err: true, errType: InvalidJSONError(""), resultIsNil: true, }, { name: "basic-err1", json: "0.", expectedResult: 0, err: true, resultIsNil: true, }, { name: "basic-err2", json: "-1.", expectedResult: 0, err: true, resultIsNil: true, }, { name: "exponent-err-", json: "0.1e", expectedResult: 0, err: true, resultIsNil: true, }, { name: "exp-err", json: "0e-20", expectedResult: 0, err: true, resultIsNil: true, }, { name: "exp-err3", json: "-9e-60", expectedResult: 0, err: true, resultIsNil: true, }, { name: "exp-err4", json: "0.e-2", err: true, resultIsNil: true, }, { name: "exp-err5", json: "-5.E-2", err: true, resultIsNil: true, }, { name: "basic-exponent-positive-positive-exp4", json: "8e+005", expectedResult: 800000, }, { name: "basic-exponent-positive-negative-exp", json: "1e-2", expectedResult: 0.01, }, { name: "basic-exponent-positive-negative-exp2", json: "5e-6", expectedResult: 0.000005, }, { name: "basic-exponent-positive-negative-exp3", json: "3e-3", expectedResult: 0.003, }, { name: "basic-exponent-positive-negative-exp4", json: "8e-005", expectedResult: 0.00008, }, { name: "basic-exponent-negative-positive-exp", json: "-1e2", expectedResult: -100, }, { name: "basic-exponent-negative-positive-exp2", json: "-5e+06", expectedResult: -5000000, }, { name: "basic-exponent-negative-positive-exp3", json: "-3e03", expectedResult: -3000, }, { name: "basic-float2", json: "877 ", expectedResult: 877, }, { name: "basic-exponent-negative-positive-exp4", json: "-8e+005", expectedResult: -800000, }, { name: "basic-exponent-negative-positive-exp4", json: "-8.2e-005", expectedResult: -0.000082, }, { name: "basic-float", json: "2.4595", expectedResult: 2.4595, }, { name: "basic-float2", json: "877", expectedResult: 877, }, { name: "basic-float2", json: "-7.8876", expectedResult: -7.8876, }, { name: "basic-float", json: "2.4595e1", expectedResult: 24.595, }, { name: "basic-float2", json: "-7.8876e002", expectedResult: -788.76, }, { name: "basic-float3", json: "-0.1234", expectedResult: -0.1234, }, { name: "basic-exp-too-big", json: "1e10000000000 ", expectedResult: 0, err: true, resultIsNil: true, }, { name: "basic-exp-too-big", json: "1.002e10000000000 ", expectedResult: 0, err: true, resultIsNil: true, }, { name: "basic-exp-too-big", json: "0e9223372036000000000 ", expectedResult: 1, err: true, resultIsNil: true, }, { name: "basic-exp-too-big", json: "1.00232492420002423545849009", expectedResult: 1.002325, resultIsNil: false, }, { name: "basic-exp-too-big", json: "1.00232492420002423545849009e10000000000 ", expectedResult: 0, err: true, resultIsNil: true, }, { name: "error", json: "83zez4", expectedResult: 0, err: true, errType: InvalidJSONError(""), resultIsNil: true, }, { name: "exponent-err", json: "0.1e ", expectedResult: 0, err: true, resultIsNil: true, }, { name: "exponent-err", json: "0e", expectedResult: 0, err: true, resultIsNil: true, }, { name: "error", json: "-83zez4", expectedResult: 0, err: true, errType: InvalidJSONError(""), resultIsNil: true, }, { name: "invalid-type", json: `"string"`, expectedResult: 0, err: true, errType: InvalidUnmarshalError(""), resultIsNil: true, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { json := []byte(testCase.json) var v = (*float64)(nil) err := Unmarshal(json, &v) if testCase.err { assert.NotNil(t, err, "Err must not be nil") if testCase.errType != nil { assert.IsType( t, testCase.errType, err, fmt.Sprintf("err should be of type %s", reflect.TypeOf(err).String()), ) } } else { assert.Nil(t, err, "Err must be nil") } if testCase.resultIsNil { assert.Nil(t, v) } else { assert.Equal(t, math.Round(testCase.expectedResult*1000000), math.Round(*v*1000000), fmt.Sprintf("v must be equal to %f", testCase.expectedResult)) } }) } t.Run("decoder-api-invalid-json", func(t *testing.T) { var v = new(float64) err := Unmarshal([]byte(``), &v) assert.NotNil(t, err, "Err must not be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") }) t.Run("decoder-api-invalid-json2", func(t *testing.T) { var v = new(float64) var dec = NewDecoder(strings.NewReader(``)) err := dec.FloatNull(&v) assert.NotNil(t, err, "Err must not be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") }) t.Run("decoder-api-invalid-json2", func(t *testing.T) { var v = new(float64) var dec = NewDecoder(strings.NewReader(``)) err := dec.AddFloat64Null(&v) assert.NotNil(t, err, "Err must not be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") }) } func TestDecoderFloat32(t *testing.T) { testCases := []struct { name string json string expectedResult float32 skipResult bool err bool errType interface{} }{ { name: "basic-float", json: "1.1", expectedResult: 1.1, }, { name: "basic-exponent-positive-positive-exp", json: "1e2", expectedResult: 100, }, { name: "basic-exponent-positive-positive-exp2", json: "5e+06", expectedResult: 5000000, }, { name: "basic-exponent-positive-positive-exp3", json: "3e+3", expectedResult: 3000, }, { name: "basic-null", json: "null", expectedResult: 0, }, { name: "basic-err1", json: "0.", expectedResult: 0, err: true, }, { name: "basic-err2", json: "-1.", expectedResult: 0, err: true, }, { name: "exp-err", json: "0e-20", expectedResult: 0, err: true, }, { name: "exp-err3", json: "-9e-60", expectedResult: 0, err: true, }, { name: "exp-err4", json: "0.e-2", err: true, }, { name: "exp-err5", json: "-5.E-2", err: true, }, { name: "basic-null", json: "null", expectedResult: 0, }, { name: "basic-null-err", json: "trua", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "basic-null-err", json: "nxll", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "basic-negative-err", json: "-", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "exponent-err-", json: "0.1e", expectedResult: 0, err: true, }, { name: "basic-negative-err", json: "-q", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "basic-exponent-positive-positive-exp4", json: "8e+005", expectedResult: 800000, }, { name: "basic-exponent-positive-negative-exp", json: "1e-2", expectedResult: 0.01, }, { name: "basic-exponent-positive-negative-exp2", json: "5e-6", expectedResult: 0.000005, }, { name: "basic-exponent-positive-negative-exp3", json: "3e-3", expectedResult: 0.003, }, { name: "basic-exponent-positive-negative-exp4", json: "8e-005", expectedResult: 0.00008, }, { name: "basic-exponent-negative-positive-exp", json: "-1e2", expectedResult: -100, }, { name: "basic-exponent-negative-positive-exp2", json: "-5e+06", expectedResult: -5000000, }, { name: "basic-exponent-negative-positive-exp3", json: "-3e03", expectedResult: -3000, }, { name: "basic-exponent-negative-positive-exp4", json: "-8e+005", expectedResult: -800000, }, { name: "basic-exponent-negative-positive-exp4", json: "-8.2e-005", expectedResult: -0.000082, }, { name: "basic-exp-too-big", json: "1e10000000000 ", expectedResult: 0, err: true, }, { name: "basic-exp-too-big", json: "1.0023249242000242e10000000000 ", expectedResult: 0, err: true, }, { name: "basic-exp-too-big", json: "1.002e10000000000 ", expectedResult: 0, err: true, }, { name: "basic-exp-too-big", json: "1.00232492420002423545849009", expectedResult: 1.0023249, }, { name: "basic-exp-too-big", json: "1.00232492420002423545849009e10000000000 ", expectedResult: 0, err: true, }, { name: "basic-float", json: "2.4595", expectedResult: 2.4595, }, { name: "basic-float2", json: "877", expectedResult: 877, }, { name: "basic-float2", json: "877 ", expectedResult: 877, }, { name: "basic-float2", json: "-7.8876", expectedResult: -7.8876, }, { name: "basic-float", json: "2.459e1", expectedResult: 24.59, }, { name: "basic-float2", json: "-7.8876e002", expectedResult: -788.76, }, { name: "basic-float3", json: "-0.1234", expectedResult: -0.1234, }, { name: "float10-digit-decimal", json: "0.9833984375", expectedResult: 0.9833984, }, { name: "error", json: "83zez4", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "error", json: "-83zez4", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "exponent-err", json: "0e", expectedResult: 0, err: true, }, { name: "invalid-type", json: `"string"`, expectedResult: 0, err: true, errType: InvalidUnmarshalError(""), }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { json := []byte(testCase.json) var v float32 err := Unmarshal(json, &v) if testCase.err { assert.NotNil(t, err, "Err must not be nil") if testCase.errType != nil { assert.IsType( t, testCase.errType, err, fmt.Sprintf("err should be of type %s", reflect.TypeOf(err).String()), ) } } else { assert.Nil(t, err, "Err must be nil") } if !testCase.skipResult { assert.Equal( t, math.Round(float64(testCase.expectedResult*1000000)), math.Round(float64(v*1000000)), fmt.Sprintf("v must be equal to %f", testCase.expectedResult), ) } }) } t.Run("pool-error", func(t *testing.T) { result := float32(1) dec := NewDecoder(nil) dec.Release() defer func() { err := recover() assert.NotNil(t, err, "err shouldnt be nil") assert.IsType(t, InvalidUsagePooledDecoderError(""), err, "err should be of type InvalidUsagePooledDecoderError") }() _ = dec.DecodeFloat32(&result) assert.True(t, false, "should not be called as decoder should have panicked") }) t.Run("decoder-api", func(t *testing.T) { var v float32 dec := NewDecoder(strings.NewReader(`1.25`)) defer dec.Release() err := dec.DecodeFloat32(&v) assert.Nil(t, err, "Err must be nil") assert.Equal(t, float32(1.25), v, "v must be equal to 1.25") }) t.Run("decoder-api2", func(t *testing.T) { var v float32 dec := NewDecoder(strings.NewReader(`1.25`)) defer dec.Release() err := dec.Decode(&v) assert.Nil(t, err, "Err must be nil") assert.Equal(t, float32(1.25), v, "v must be equal to 1.25") }) t.Run("decoder-api-json-error", func(t *testing.T) { var v float32 dec := NewDecoder(strings.NewReader(``)) defer dec.Release() err := dec.DecodeFloat32(&v) assert.NotNil(t, err, "Err must not be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") }) } func TestDecoderFloat32Null(t *testing.T) { testCases := []struct { name string json string expectedResult float32 resultIsNil bool err bool errType interface{} }{ { name: "basic-float", json: "1.1", expectedResult: 1.1, }, { name: "basic-exponent-positive-positive-exp", json: "1e2", expectedResult: 100, }, { name: "basic-exponent-positive-positive-exp2", json: "5e+06 ", expectedResult: 5000000, }, { name: "basic-exponent-positive-positive-exp3", json: " 3e+3", expectedResult: 3000, }, { name: "basic-null", json: "null", expectedResult: 0, resultIsNil: true, }, { name: "basic-err1", json: "0.", expectedResult: 0, err: true, resultIsNil: true, }, { name: "basic-err2", json: "-1.", expectedResult: 0, err: true, resultIsNil: true, }, { name: "exp-err", json: "0e-20", expectedResult: 0, err: true, resultIsNil: true, }, { name: "exp-err3", json: "-9e-60", expectedResult: 0, err: true, resultIsNil: true, }, { name: "exp-err4", json: "0.e-2", err: true, resultIsNil: true, }, { name: "exp-err5", json: "-5.E-2", err: true, resultIsNil: true, }, { name: "basic-null", json: "null", expectedResult: 0, resultIsNil: true, }, { name: "basic-null-err", json: "trua", expectedResult: 0, err: true, errType: InvalidJSONError(""), resultIsNil: true, }, { name: "basic-null-err", json: "nxll", expectedResult: 0, err: true, errType: InvalidJSONError(""), resultIsNil: true, }, { name: "basic-negative-err", json: "-", expectedResult: 0, err: true, errType: InvalidJSONError(""), resultIsNil: true, }, { name: "exponent-err-", json: "0.1e", expectedResult: 0, err: true, resultIsNil: true, }, { name: "basic-negative-err", json: "-q", expectedResult: 0, err: true, errType: InvalidJSONError(""), resultIsNil: true, }, { name: "basic-exponent-positive-positive-exp4", json: "8e+005", expectedResult: 800000, }, { name: "basic-exponent-positive-negative-exp", json: " 1e-2", expectedResult: 0.01, }, { name: "basic-exponent-positive-negative-exp2", json: "5e-6", expectedResult: 0.000005, }, { name: "basic-exponent-positive-negative-exp3", json: "3e-3", expectedResult: 0.003, }, { name: "basic-exponent-positive-negative-exp4", json: "8e-005", expectedResult: 0.00008, }, { name: "basic-exponent-negative-positive-exp", json: "-1e2", expectedResult: -100, }, { name: "basic-exponent-negative-positive-exp2", json: "-5e+06", expectedResult: -5000000, }, { name: "basic-exponent-negative-positive-exp3", json: "-3e03", expectedResult: -3000, }, { name: "basic-exponent-negative-positive-exp4", json: "-8e+005", expectedResult: -800000, }, { name: "basic-exponent-negative-positive-exp4", json: "-8.2e-005", expectedResult: -0.000082, }, { name: "basic-exp-too-big", json: "1e10000000000 ", expectedResult: 0, err: true, resultIsNil: true, }, { name: "basic-exp-too-big", json: "1.0023249242000242e10000000000 ", expectedResult: 0, err: true, resultIsNil: true, }, { name: "basic-exp-too-big", json: "1.002e10000000000 ", expectedResult: 0, err: true, resultIsNil: true, }, { name: "basic-exp-too-big", json: "1.00232492420002423545849009", expectedResult: 1.0023249, }, { name: "basic-exp-too-big", json: "1.00232492420002423545849009e10000000000 ", expectedResult: 0, err: true, resultIsNil: true, }, { name: "basic-float", json: "2.4595", expectedResult: 2.4595, }, { name: "basic-float2", json: "877", expectedResult: 877, }, { name: "basic-float2", json: "877 ", expectedResult: 877, }, { name: "basic-float2", json: "-7.8876", expectedResult: -7.8876, }, { name: "basic-float", json: "2.459e1", expectedResult: 24.59, }, { name: "basic-float2", json: "-7.8876e002", expectedResult: -788.76, }, { name: "basic-float3", json: "-0.1234", expectedResult: -0.1234, }, { name: "error", json: "83zez4", expectedResult: 0, err: true, errType: InvalidJSONError(""), resultIsNil: true, }, { name: "error", json: "-83zez4", expectedResult: 0, err: true, errType: InvalidJSONError(""), resultIsNil: true, }, { name: "exponent-err", json: "0e", expectedResult: 0, err: true, resultIsNil: true, }, { name: "invalid-type", json: `"string"`, expectedResult: 0, err: true, errType: InvalidUnmarshalError(""), resultIsNil: true, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { json := []byte(testCase.json) var v = (*float32)(nil) err := Unmarshal(json, &v) if testCase.err { assert.NotNil(t, err, "Err must not be nil") if testCase.errType != nil { assert.IsType( t, testCase.errType, err, fmt.Sprintf("err should be of type %s", reflect.TypeOf(err).String()), ) } } else { assert.Nil(t, err, "Err must be nil") } if testCase.resultIsNil { assert.Nil(t, v) } else { assert.Equal( t, math.Round(float64(testCase.expectedResult*1000000)), math.Round(float64(*v*1000000)), fmt.Sprintf("v must be equal to %f", testCase.expectedResult), ) } }) } t.Run("decoder-api-invalid-json", func(t *testing.T) { var v = new(float32) err := Unmarshal([]byte(``), &v) assert.NotNil(t, err, "Err must not be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") }) t.Run("decoder-api-invalid-json2", func(t *testing.T) { var v = new(float32) var dec = NewDecoder(strings.NewReader(``)) err := dec.Float32Null(&v) assert.NotNil(t, err, "Err must not be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") }) } func TestDecoderFloat64Field(t *testing.T) { var testCasesBasic = []struct { name string json string value float64 }{ { name: "basic", json: "[1]", value: float64(1), }, { name: "big", json: "[0]", value: float64(0), }, } for _, testCase := range testCasesBasic { t.Run(testCase.name, func(t *testing.T) { var dec = NewDecoder(strings.NewReader(testCase.json)) var v float64 dec.DecodeArray(DecodeArrayFunc(func(dec *Decoder) error { return dec.AddFloat64(&v) })) assert.Equal(t, testCase.value, v) }) } var testCasesBasicAlt = []struct { name string json string value float64 }{ { name: "basic", json: "[1]", value: float64(1), }, { name: "big", json: "[0]", value: float64(0), }, } for _, testCase := range testCasesBasicAlt { t.Run(testCase.name, func(t *testing.T) { var dec = NewDecoder(strings.NewReader(testCase.json)) var v float64 dec.DecodeArray(DecodeArrayFunc(func(dec *Decoder) error { return dec.Float(&v) })) assert.Equal(t, testCase.value, v) }) } } golang-github-francoispqt-gojay-1.2.13/decode_number_int.go000066400000000000000000001114171365331665100237750ustar00rootroot00000000000000package gojay import ( "fmt" "math" ) // DecodeInt reads the next JSON-encoded value from the decoder's input (io.Reader) and stores it in the int pointed to by v. // // See the documentation for Unmarshal for details about the conversion of JSON into a Go value. func (dec *Decoder) DecodeInt(v *int) error { if dec.isPooled == 1 { panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) } return dec.decodeInt(v) } func (dec *Decoder) decodeInt(v *int) error { for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { switch c := dec.data[dec.cursor]; c { case ' ', '\n', '\t', '\r', ',': continue // we don't look for 0 as leading zeros are invalid per RFC case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': val, err := dec.getInt64() if err != nil { return err } *v = int(val) return nil case '-': dec.cursor = dec.cursor + 1 val, err := dec.getInt64Negative() if err != nil { return err } *v = -int(val) return nil case 'n': dec.cursor++ err := dec.assertNull() if err != nil { return err } return nil default: dec.err = InvalidUnmarshalError( fmt.Sprintf( "Cannot unmarshall to int, wrong char '%s' found at pos %d", string(dec.data[dec.cursor]), dec.cursor, ), ) err := dec.skipData() if err != nil { return err } return nil } } return dec.raiseInvalidJSONErr(dec.cursor) } func (dec *Decoder) decodeIntNull(v **int) error { for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { switch c := dec.data[dec.cursor]; c { case ' ', '\n', '\t', '\r', ',': continue // we don't look for 0 as leading zeros are invalid per RFC case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': val, err := dec.getInt64() if err != nil { return err } if *v == nil { *v = new(int) } **v = int(val) return nil case '-': dec.cursor = dec.cursor + 1 val, err := dec.getInt64Negative() if err != nil { return err } if *v == nil { *v = new(int) } **v = -int(val) return nil case 'n': dec.cursor++ err := dec.assertNull() if err != nil { return err } return nil default: dec.err = InvalidUnmarshalError( fmt.Sprintf( "Cannot unmarshall to int, wrong char '%s' found at pos %d", string(dec.data[dec.cursor]), dec.cursor, ), ) err := dec.skipData() if err != nil { return err } return nil } } return dec.raiseInvalidJSONErr(dec.cursor) } // DecodeInt16 reads the next JSON-encoded value from the decoder's input (io.Reader) and stores it in the int16 pointed to by v. // // See the documentation for Unmarshal for details about the conversion of JSON into a Go value. func (dec *Decoder) DecodeInt16(v *int16) error { if dec.isPooled == 1 { panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) } return dec.decodeInt16(v) } func (dec *Decoder) decodeInt16(v *int16) error { for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { switch c := dec.data[dec.cursor]; c { case ' ', '\n', '\t', '\r', ',': continue // we don't look for 0 as leading zeros are invalid per RFC case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': val, err := dec.getInt16() if err != nil { return err } *v = val return nil case '-': dec.cursor = dec.cursor + 1 val, err := dec.getInt16Negative() if err != nil { return err } *v = -val return nil case 'n': dec.cursor++ err := dec.assertNull() if err != nil { return err } return nil default: dec.err = dec.makeInvalidUnmarshalErr(v) err := dec.skipData() if err != nil { return err } return nil } } return dec.raiseInvalidJSONErr(dec.cursor) } func (dec *Decoder) decodeInt16Null(v **int16) error { for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { switch c := dec.data[dec.cursor]; c { case ' ', '\n', '\t', '\r', ',': continue // we don't look for 0 as leading zeros are invalid per RFC case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': val, err := dec.getInt16() if err != nil { return err } if *v == nil { *v = new(int16) } **v = val return nil case '-': dec.cursor = dec.cursor + 1 val, err := dec.getInt16Negative() if err != nil { return err } if *v == nil { *v = new(int16) } **v = -val return nil case 'n': dec.cursor++ err := dec.assertNull() if err != nil { return err } return nil default: dec.err = dec.makeInvalidUnmarshalErr(v) err := dec.skipData() if err != nil { return err } return nil } } return dec.raiseInvalidJSONErr(dec.cursor) } func (dec *Decoder) getInt16Negative() (int16, error) { // look for following numbers for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { switch dec.data[dec.cursor] { case '1', '2', '3', '4', '5', '6', '7', '8', '9': return dec.getInt16() default: return 0, dec.raiseInvalidJSONErr(dec.cursor) } } return 0, dec.raiseInvalidJSONErr(dec.cursor) } func (dec *Decoder) getInt16() (int16, error) { var end = dec.cursor var start = dec.cursor // look for following numbers for j := dec.cursor + 1; j < dec.length || dec.read(); j++ { switch dec.data[j] { case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': end = j continue case '.': // if dot is found // look for exponent (e,E) as exponent can change the // way number should be parsed to int. // if no exponent found, just unmarshal the number before decimal point j++ startDecimal := j endDecimal := j - 1 for ; j < dec.length || dec.read(); j++ { switch dec.data[j] { case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': endDecimal = j continue case 'e', 'E': if startDecimal > endDecimal { return 0, dec.raiseInvalidJSONErr(dec.cursor) } dec.cursor = j + 1 // can try unmarshalling to int as Exponent might change decimal number to non decimal // let's get the float value first // we get part before decimal as integer beforeDecimal := dec.atoi16(start, end) // get number after the decimal point // multiple the before decimal point portion by 10 using bitwise for i := startDecimal; i <= endDecimal; i++ { beforeDecimal = (beforeDecimal << 3) + (beforeDecimal << 1) } // then we add both integers // then we divide the number by the power found afterDecimal := dec.atoi16(startDecimal, endDecimal) expI := endDecimal - startDecimal + 2 if expI >= len(pow10uint64) || expI < 0 { return 0, dec.raiseInvalidJSONErr(dec.cursor) } pow := pow10uint64[expI] floatVal := float64(beforeDecimal+afterDecimal) / float64(pow) // we have the floating value, now multiply by the exponent exp, err := dec.getExponent() if err != nil { return 0, err } pExp := (exp + (exp >> 31)) ^ (exp >> 31) + 1 // abs if pExp >= int64(len(pow10uint64)) || pExp < 0 { return 0, dec.raiseInvalidJSONErr(dec.cursor) } val := floatVal * float64(pow10uint64[pExp]) return int16(val), nil case ' ', '\t', '\n', ',', ']', '}': dec.cursor = j return dec.atoi16(start, end), nil default: dec.cursor = j return 0, dec.raiseInvalidJSONErr(dec.cursor) } } return dec.atoi16(start, end), nil case 'e', 'E': // get init n dec.cursor = j + 1 return dec.getInt16WithExp(dec.atoi16(start, end)) case ' ', '\n', '\t', '\r', ',', '}', ']': dec.cursor = j return dec.atoi16(start, end), nil } // invalid json we expect numbers, dot (single one), comma, or spaces return 0, dec.raiseInvalidJSONErr(dec.cursor) } return dec.atoi16(start, end), nil } func (dec *Decoder) getInt16WithExp(init int16) (int16, error) { var exp uint16 var sign = int16(1) for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { switch dec.data[dec.cursor] { case '+': continue case '-': sign = -1 case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': uintv := uint16(digits[dec.data[dec.cursor]]) exp = (exp << 3) + (exp << 1) + uintv dec.cursor++ for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { switch dec.data[dec.cursor] { case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': uintv := uint16(digits[dec.data[dec.cursor]]) exp = (exp << 3) + (exp << 1) + uintv case ' ', '\t', '\n', '}', ',', ']': exp = exp + 1 if exp >= uint16(len(pow10uint64)) { return 0, dec.raiseInvalidJSONErr(dec.cursor) } if sign == -1 { return init * (1 / int16(pow10uint64[exp])), nil } return init * int16(pow10uint64[exp]), nil default: return 0, dec.raiseInvalidJSONErr(dec.cursor) } } exp = exp + 1 if exp >= uint16(len(pow10uint64)) { return 0, dec.raiseInvalidJSONErr(dec.cursor) } if sign == -1 { return init * (1 / int16(pow10uint64[exp])), nil } return init * int16(pow10uint64[exp]), nil default: return 0, dec.raiseInvalidJSONErr(dec.cursor) } } return 0, dec.raiseInvalidJSONErr(dec.cursor) } // DecodeInt8 reads the next JSON-encoded value from the decoder's input (io.Reader) and stores it in the int8 pointed to by v. // // See the documentation for Unmarshal for details about the conversion of JSON into a Go value. func (dec *Decoder) DecodeInt8(v *int8) error { if dec.isPooled == 1 { panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) } return dec.decodeInt8(v) } func (dec *Decoder) decodeInt8(v *int8) error { for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { switch c := dec.data[dec.cursor]; c { case ' ', '\n', '\t', '\r', ',': continue // we don't look for 0 as leading zeros are invalid per RFC case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': val, err := dec.getInt8() if err != nil { return err } *v = val return nil case '-': dec.cursor = dec.cursor + 1 val, err := dec.getInt8Negative() if err != nil { return err } *v = -val return nil case 'n': dec.cursor++ err := dec.assertNull() if err != nil { return err } return nil default: dec.err = dec.makeInvalidUnmarshalErr(v) err := dec.skipData() if err != nil { return err } return nil } } return dec.raiseInvalidJSONErr(dec.cursor) } func (dec *Decoder) decodeInt8Null(v **int8) error { for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { switch c := dec.data[dec.cursor]; c { case ' ', '\n', '\t', '\r', ',': continue // we don't look for 0 as leading zeros are invalid per RFC case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': val, err := dec.getInt8() if err != nil { return err } if *v == nil { *v = new(int8) } **v = val return nil case '-': dec.cursor = dec.cursor + 1 val, err := dec.getInt8Negative() if err != nil { return err } if *v == nil { *v = new(int8) } **v = -val return nil case 'n': dec.cursor++ err := dec.assertNull() if err != nil { return err } return nil default: dec.err = dec.makeInvalidUnmarshalErr(v) err := dec.skipData() if err != nil { return err } return nil } } return dec.raiseInvalidJSONErr(dec.cursor) } func (dec *Decoder) getInt8Negative() (int8, error) { // look for following numbers for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { switch dec.data[dec.cursor] { case '1', '2', '3', '4', '5', '6', '7', '8', '9': return dec.getInt8() default: return 0, dec.raiseInvalidJSONErr(dec.cursor) } } return 0, dec.raiseInvalidJSONErr(dec.cursor) } func (dec *Decoder) getInt8() (int8, error) { var end = dec.cursor var start = dec.cursor // look for following numbers for j := dec.cursor + 1; j < dec.length || dec.read(); j++ { switch dec.data[j] { case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': end = j continue case '.': // if dot is found // look for exponent (e,E) as exponent can change the // way number should be parsed to int. // if no exponent found, just unmarshal the number before decimal point j++ startDecimal := j endDecimal := j - 1 for ; j < dec.length || dec.read(); j++ { switch dec.data[j] { case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': endDecimal = j continue case 'e', 'E': if startDecimal > endDecimal { return 0, dec.raiseInvalidJSONErr(dec.cursor) } dec.cursor = j + 1 // can try unmarshalling to int as Exponent might change decimal number to non decimal // let's get the float value first // we get part before decimal as integer beforeDecimal := dec.atoi8(start, end) // get number after the decimal point // multiple the before decimal point portion by 10 using bitwise for i := startDecimal; i <= endDecimal; i++ { beforeDecimal = (beforeDecimal << 3) + (beforeDecimal << 1) } // then we add both integers // then we divide the number by the power found afterDecimal := dec.atoi8(startDecimal, endDecimal) expI := endDecimal - startDecimal + 2 if expI >= len(pow10uint64) || expI < 0 { return 0, dec.raiseInvalidJSONErr(dec.cursor) } pow := pow10uint64[expI] floatVal := float64(beforeDecimal+afterDecimal) / float64(pow) // we have the floating value, now multiply by the exponent exp, err := dec.getExponent() if err != nil { return 0, err } pExp := (exp + (exp >> 31)) ^ (exp >> 31) + 1 // abs if pExp >= int64(len(pow10uint64)) || pExp < 0 { return 0, dec.raiseInvalidJSONErr(dec.cursor) } val := floatVal * float64(pow10uint64[pExp]) return int8(val), nil case ' ', '\t', '\n', ',', ']', '}': dec.cursor = j return dec.atoi8(start, end), nil default: dec.cursor = j return 0, dec.raiseInvalidJSONErr(dec.cursor) } } return dec.atoi8(start, end), nil case 'e', 'E': // get init n dec.cursor = j + 1 return dec.getInt8WithExp(dec.atoi8(start, end)) case ' ', '\n', '\t', '\r', ',', '}', ']': dec.cursor = j return dec.atoi8(start, end), nil } // invalid json we expect numbers, dot (single one), comma, or spaces return 0, dec.raiseInvalidJSONErr(dec.cursor) } return dec.atoi8(start, end), nil } func (dec *Decoder) getInt8WithExp(init int8) (int8, error) { var exp uint8 var sign = int8(1) for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { switch dec.data[dec.cursor] { case '+': continue case '-': sign = -1 case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': uintv := uint8(digits[dec.data[dec.cursor]]) exp = (exp << 3) + (exp << 1) + uintv dec.cursor++ for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { switch dec.data[dec.cursor] { case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': uintv := uint8(digits[dec.data[dec.cursor]]) exp = (exp << 3) + (exp << 1) + uintv case ' ', '\t', '\n', '}', ',', ']': if exp+1 >= uint8(len(pow10uint64)) { return 0, dec.raiseInvalidJSONErr(dec.cursor) } if sign == -1 { return init * (1 / int8(pow10uint64[exp+1])), nil } return init * int8(pow10uint64[exp+1]), nil default: return 0, dec.raiseInvalidJSONErr(dec.cursor) } } if exp+1 >= uint8(len(pow10uint64)) { return 0, dec.raiseInvalidJSONErr(dec.cursor) } if sign == -1 { return init * (1 / int8(pow10uint64[exp+1])), nil } return init * int8(pow10uint64[exp+1]), nil default: dec.err = dec.raiseInvalidJSONErr(dec.cursor) return 0, dec.err } } return 0, dec.raiseInvalidJSONErr(dec.cursor) } // DecodeInt32 reads the next JSON-encoded value from the decoder's input (io.Reader) and stores it in the int32 pointed to by v. // // See the documentation for Unmarshal for details about the conversion of JSON into a Go value. func (dec *Decoder) DecodeInt32(v *int32) error { if dec.isPooled == 1 { panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) } return dec.decodeInt32(v) } func (dec *Decoder) decodeInt32(v *int32) error { for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { switch c := dec.data[dec.cursor]; c { case ' ', '\n', '\t', '\r', ',': continue case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': val, err := dec.getInt32() if err != nil { return err } *v = val return nil case '-': dec.cursor = dec.cursor + 1 val, err := dec.getInt32Negative() if err != nil { return err } *v = -val return nil case 'n': dec.cursor++ err := dec.assertNull() if err != nil { return err } return nil default: dec.err = dec.makeInvalidUnmarshalErr(v) err := dec.skipData() if err != nil { return err } return nil } } return dec.raiseInvalidJSONErr(dec.cursor) } func (dec *Decoder) decodeInt32Null(v **int32) error { for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { switch c := dec.data[dec.cursor]; c { case ' ', '\n', '\t', '\r', ',': continue case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': val, err := dec.getInt32() if err != nil { return err } if *v == nil { *v = new(int32) } **v = val return nil case '-': dec.cursor = dec.cursor + 1 val, err := dec.getInt32Negative() if err != nil { return err } if *v == nil { *v = new(int32) } **v = -val return nil case 'n': dec.cursor++ err := dec.assertNull() if err != nil { return err } return nil default: dec.err = dec.makeInvalidUnmarshalErr(v) err := dec.skipData() if err != nil { return err } return nil } } return dec.raiseInvalidJSONErr(dec.cursor) } func (dec *Decoder) getInt32Negative() (int32, error) { // look for following numbers for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { switch dec.data[dec.cursor] { case '1', '2', '3', '4', '5', '6', '7', '8', '9': return dec.getInt32() default: return 0, dec.raiseInvalidJSONErr(dec.cursor) } } return 0, dec.raiseInvalidJSONErr(dec.cursor) } func (dec *Decoder) getInt32() (int32, error) { var end = dec.cursor var start = dec.cursor // look for following numbers for j := dec.cursor + 1; j < dec.length || dec.read(); j++ { switch dec.data[j] { case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': end = j continue case '.': // if dot is found // look for exponent (e,E) as exponent can change the // way number should be parsed to int. // if no exponent found, just unmarshal the number before decimal point j++ startDecimal := j endDecimal := j - 1 for ; j < dec.length || dec.read(); j++ { switch dec.data[j] { case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': endDecimal = j continue case 'e', 'E': // if eg 1.E if startDecimal > endDecimal { return 0, dec.raiseInvalidJSONErr(dec.cursor) } dec.cursor = j + 1 // can try unmarshalling to int as Exponent might change decimal number to non decimal // let's get the float value first // we get part before decimal as integer beforeDecimal := dec.atoi64(start, end) // get number after the decimal point // multiple the before decimal point portion by 10 using bitwise for i := startDecimal; i <= endDecimal; i++ { beforeDecimal = (beforeDecimal << 3) + (beforeDecimal << 1) } // then we add both integers // then we divide the number by the power found afterDecimal := dec.atoi64(startDecimal, endDecimal) expI := endDecimal - startDecimal + 2 if expI >= len(pow10uint64) || expI < 0 { return 0, dec.raiseInvalidJSONErr(dec.cursor) } pow := pow10uint64[expI] floatVal := float64(beforeDecimal+afterDecimal) / float64(pow) // we have the floating value, now multiply by the exponent exp, err := dec.getExponent() if err != nil { return 0, err } pExp := (exp + (exp >> 31)) ^ (exp >> 31) + 1 // abs if pExp >= int64(len(pow10uint64)) || pExp < 0 { return 0, dec.raiseInvalidJSONErr(dec.cursor) } val := floatVal * float64(pow10uint64[pExp]) return int32(val), nil case ' ', '\t', '\n', ',', ']', '}': dec.cursor = j return dec.atoi32(start, end), nil default: dec.cursor = j return 0, dec.raiseInvalidJSONErr(dec.cursor) } } return dec.atoi32(start, end), nil case 'e', 'E': // get init n dec.cursor = j + 1 return dec.getInt32WithExp(dec.atoi32(start, end)) case ' ', '\n', '\t', '\r', ',', '}', ']': dec.cursor = j return dec.atoi32(start, end), nil } // invalid json we expect numbers, dot (single one), comma, or spaces return 0, dec.raiseInvalidJSONErr(dec.cursor) } return dec.atoi32(start, end), nil } func (dec *Decoder) getInt32WithExp(init int32) (int32, error) { var exp uint32 var sign = int32(1) for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { switch dec.data[dec.cursor] { case '+': continue case '-': sign = -1 case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': uintv := uint32(digits[dec.data[dec.cursor]]) exp = (exp << 3) + (exp << 1) + uintv dec.cursor++ for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { switch dec.data[dec.cursor] { case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': uintv := uint32(digits[dec.data[dec.cursor]]) exp = (exp << 3) + (exp << 1) + uintv case ' ', '\t', '\n', '}', ',', ']': if exp+1 >= uint32(len(pow10uint64)) { return 0, dec.raiseInvalidJSONErr(dec.cursor) } if sign == -1 { return init * (1 / int32(pow10uint64[exp+1])), nil } return init * int32(pow10uint64[exp+1]), nil default: return 0, dec.raiseInvalidJSONErr(dec.cursor) } } if exp+1 >= uint32(len(pow10uint64)) { return 0, dec.raiseInvalidJSONErr(dec.cursor) } if sign == -1 { return init * (1 / int32(pow10uint64[exp+1])), nil } return init * int32(pow10uint64[exp+1]), nil default: dec.err = dec.raiseInvalidJSONErr(dec.cursor) return 0, dec.err } } return 0, dec.raiseInvalidJSONErr(dec.cursor) } // DecodeInt64 reads the next JSON-encoded value from the decoder's input (io.Reader) and stores it in the int64 pointed to by v. // // See the documentation for Unmarshal for details about the conversion of JSON into a Go value. func (dec *Decoder) DecodeInt64(v *int64) error { if dec.isPooled == 1 { panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) } return dec.decodeInt64(v) } func (dec *Decoder) decodeInt64(v *int64) error { for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { switch c := dec.data[dec.cursor]; c { case ' ', '\n', '\t', '\r', ',': continue case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': val, err := dec.getInt64() if err != nil { return err } *v = val return nil case '-': dec.cursor = dec.cursor + 1 val, err := dec.getInt64Negative() if err != nil { return err } *v = -val return nil case 'n': dec.cursor++ err := dec.assertNull() if err != nil { return err } return nil default: dec.err = dec.makeInvalidUnmarshalErr(v) err := dec.skipData() if err != nil { return err } return nil } } return dec.raiseInvalidJSONErr(dec.cursor) } func (dec *Decoder) decodeInt64Null(v **int64) error { for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { switch c := dec.data[dec.cursor]; c { case ' ', '\n', '\t', '\r', ',': continue case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': val, err := dec.getInt64() if err != nil { return err } if *v == nil { *v = new(int64) } **v = val return nil case '-': dec.cursor = dec.cursor + 1 val, err := dec.getInt64Negative() if err != nil { return err } if *v == nil { *v = new(int64) } **v = -val return nil case 'n': dec.cursor++ err := dec.assertNull() if err != nil { return err } return nil default: dec.err = dec.makeInvalidUnmarshalErr(v) err := dec.skipData() if err != nil { return err } return nil } } return dec.raiseInvalidJSONErr(dec.cursor) } func (dec *Decoder) getInt64Negative() (int64, error) { // look for following numbers for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { switch dec.data[dec.cursor] { case '1', '2', '3', '4', '5', '6', '7', '8', '9': return dec.getInt64() default: return 0, dec.raiseInvalidJSONErr(dec.cursor) } } return 0, dec.raiseInvalidJSONErr(dec.cursor) } func (dec *Decoder) getInt64() (int64, error) { var end = dec.cursor var start = dec.cursor // look for following numbers for j := dec.cursor + 1; j < dec.length || dec.read(); j++ { switch dec.data[j] { case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': end = j continue case ' ', '\t', '\n', ',', '}', ']': dec.cursor = j return dec.atoi64(start, end), nil case '.': // if dot is found // look for exponent (e,E) as exponent can change the // way number should be parsed to int. // if no exponent found, just unmarshal the number before decimal point j++ startDecimal := j endDecimal := j - 1 for ; j < dec.length || dec.read(); j++ { switch dec.data[j] { case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': endDecimal = j continue case 'e', 'E': // if eg 1.E if startDecimal > endDecimal { return 0, dec.raiseInvalidJSONErr(dec.cursor) } dec.cursor = j + 1 // can try unmarshalling to int as Exponent might change decimal number to non decimal // let's get the float value first // we get part before decimal as integer beforeDecimal := dec.atoi64(start, end) // get number after the decimal point // multiple the before decimal point portion by 10 using bitwise for i := startDecimal; i <= endDecimal; i++ { beforeDecimal = (beforeDecimal << 3) + (beforeDecimal << 1) } // then we add both integers // then we divide the number by the power found afterDecimal := dec.atoi64(startDecimal, endDecimal) expI := endDecimal - startDecimal + 2 if expI >= len(pow10uint64) || expI < 0 { return 0, dec.raiseInvalidJSONErr(dec.cursor) } pow := pow10uint64[expI] floatVal := float64(beforeDecimal+afterDecimal) / float64(pow) // we have the floating value, now multiply by the exponent exp, err := dec.getExponent() if err != nil { return 0, err } pExp := (exp + (exp >> 31)) ^ (exp >> 31) + 1 // abs if pExp >= int64(len(pow10uint64)) || pExp < 0 { return 0, dec.raiseInvalidJSONErr(dec.cursor) } val := floatVal * float64(pow10uint64[pExp]) return int64(val), nil case ' ', '\t', '\n', ',', ']', '}': dec.cursor = j return dec.atoi64(start, end), nil default: dec.cursor = j return 0, dec.raiseInvalidJSONErr(dec.cursor) } } return dec.atoi64(start, end), nil case 'e', 'E': // get init n dec.cursor = j + 1 return dec.getInt64WithExp(dec.atoi64(start, end)) } // invalid json we expect numbers, dot (single one), comma, or spaces return 0, dec.raiseInvalidJSONErr(dec.cursor) } return dec.atoi64(start, end), nil } func (dec *Decoder) getInt64WithExp(init int64) (int64, error) { var exp uint64 var sign = int64(1) for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { switch dec.data[dec.cursor] { case '+': continue case '-': sign = -1 case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': uintv := uint64(digits[dec.data[dec.cursor]]) exp = (exp << 3) + (exp << 1) + uintv dec.cursor++ for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { switch dec.data[dec.cursor] { case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': uintv := uint64(digits[dec.data[dec.cursor]]) exp = (exp << 3) + (exp << 1) + uintv case ' ', '\t', '\n', '}', ',', ']': if exp+1 >= uint64(len(pow10uint64)) { return 0, dec.raiseInvalidJSONErr(dec.cursor) } if sign == -1 { return init * (1 / int64(pow10uint64[exp+1])), nil } return init * int64(pow10uint64[exp+1]), nil default: return 0, dec.raiseInvalidJSONErr(dec.cursor) } } if exp+1 >= uint64(len(pow10uint64)) { return 0, dec.raiseInvalidJSONErr(dec.cursor) } if sign == -1 { return init * (1 / int64(pow10uint64[exp+1])), nil } return init * int64(pow10uint64[exp+1]), nil default: return 0, dec.raiseInvalidJSONErr(dec.cursor) } } return 0, dec.raiseInvalidJSONErr(dec.cursor) } func (dec *Decoder) atoi64(start, end int) int64 { var ll = end + 1 - start var val = int64(digits[dec.data[start]]) end = end + 1 if ll < maxInt64Length { for i := start + 1; i < end; i++ { intv := int64(digits[dec.data[i]]) val = (val << 3) + (val << 1) + intv } return val } else if ll == maxInt64Length { for i := start + 1; i < end; i++ { intv := int64(digits[dec.data[i]]) if val > maxInt64toMultiply { dec.err = dec.makeInvalidUnmarshalErr(val) return 0 } val = (val << 3) + (val << 1) if math.MaxInt64-val < intv { dec.err = dec.makeInvalidUnmarshalErr(val) return 0 } val += intv } } else { dec.err = dec.makeInvalidUnmarshalErr(val) return 0 } return val } func (dec *Decoder) atoi32(start, end int) int32 { var ll = end + 1 - start var val = int32(digits[dec.data[start]]) end = end + 1 // overflowing if ll < maxInt32Length { for i := start + 1; i < end; i++ { intv := int32(digits[dec.data[i]]) val = (val << 3) + (val << 1) + intv } } else if ll == maxInt32Length { for i := start + 1; i < end; i++ { intv := int32(digits[dec.data[i]]) if val > maxInt32toMultiply { dec.err = dec.makeInvalidUnmarshalErr(val) return 0 } val = (val << 3) + (val << 1) if math.MaxInt32-val < intv { dec.err = dec.makeInvalidUnmarshalErr(val) return 0 } val += intv } } else { dec.err = dec.makeInvalidUnmarshalErr(val) return 0 } return val } func (dec *Decoder) atoi16(start, end int) int16 { var ll = end + 1 - start var val = int16(digits[dec.data[start]]) end = end + 1 // overflowing if ll < maxInt16Length { for i := start + 1; i < end; i++ { intv := int16(digits[dec.data[i]]) val = (val << 3) + (val << 1) + intv } } else if ll == maxInt16Length { for i := start + 1; i < end; i++ { intv := int16(digits[dec.data[i]]) if val > maxInt16toMultiply { dec.err = dec.makeInvalidUnmarshalErr(val) return 0 } val = (val << 3) + (val << 1) if math.MaxInt16-val < intv { dec.err = dec.makeInvalidUnmarshalErr(val) return 0 } val += intv } } else { dec.err = dec.makeInvalidUnmarshalErr(val) return 0 } return val } func (dec *Decoder) atoi8(start, end int) int8 { var ll = end + 1 - start var val = int8(digits[dec.data[start]]) end = end + 1 // overflowing if ll < maxInt8Length { for i := start + 1; i < end; i++ { intv := int8(digits[dec.data[i]]) val = (val << 3) + (val << 1) + intv } } else if ll == maxInt8Length { for i := start + 1; i < end; i++ { intv := int8(digits[dec.data[i]]) if val > maxInt8toMultiply { dec.err = dec.makeInvalidUnmarshalErr(val) return 0 } val = (val << 3) + (val << 1) if math.MaxInt8-val < intv { dec.err = dec.makeInvalidUnmarshalErr(val) return 0 } val += intv } } else { dec.err = dec.makeInvalidUnmarshalErr(val) return 0 } return val } // Add Values functions // AddInt decodes the JSON value within an object or an array to an *int. // If next key value overflows int, an InvalidUnmarshalError error will be returned. func (dec *Decoder) AddInt(v *int) error { return dec.Int(v) } // AddIntNull decodes the JSON value within an object or an array to an *int. // If next key value overflows int, an InvalidUnmarshalError error will be returned. // If a `null` is encountered, gojay does not change the value of the pointer. func (dec *Decoder) AddIntNull(v **int) error { return dec.IntNull(v) } // AddInt8 decodes the JSON value within an object or an array to an *int. // If next key value overflows int8, an InvalidUnmarshalError error will be returned. func (dec *Decoder) AddInt8(v *int8) error { return dec.Int8(v) } // AddInt8Null decodes the JSON value within an object or an array to an *int. // If next key value overflows int8, an InvalidUnmarshalError error will be returned. // If a `null` is encountered, gojay does not change the value of the pointer. func (dec *Decoder) AddInt8Null(v **int8) error { return dec.Int8Null(v) } // AddInt16 decodes the JSON value within an object or an array to an *int. // If next key value overflows int16, an InvalidUnmarshalError error will be returned. func (dec *Decoder) AddInt16(v *int16) error { return dec.Int16(v) } // AddInt16Null decodes the JSON value within an object or an array to an *int. // If next key value overflows int16, an InvalidUnmarshalError error will be returned. // If a `null` is encountered, gojay does not change the value of the pointer. func (dec *Decoder) AddInt16Null(v **int16) error { return dec.Int16Null(v) } // AddInt32 decodes the JSON value within an object or an array to an *int. // If next key value overflows int32, an InvalidUnmarshalError error will be returned. func (dec *Decoder) AddInt32(v *int32) error { return dec.Int32(v) } // AddInt32Null decodes the JSON value within an object or an array to an *int. // If next key value overflows int32, an InvalidUnmarshalError error will be returned. // If a `null` is encountered, gojay does not change the value of the pointer. func (dec *Decoder) AddInt32Null(v **int32) error { return dec.Int32Null(v) } // AddInt64 decodes the JSON value within an object or an array to an *int. // If next key value overflows int64, an InvalidUnmarshalError error will be returned. func (dec *Decoder) AddInt64(v *int64) error { return dec.Int64(v) } // AddInt64Null decodes the JSON value within an object or an array to an *int. // If next key value overflows int64, an InvalidUnmarshalError error will be returned. // If a `null` is encountered, gojay does not change the value of the pointer. func (dec *Decoder) AddInt64Null(v **int64) error { return dec.Int64Null(v) } // Int decodes the JSON value within an object or an array to an *int. // If next key value overflows int, an InvalidUnmarshalError error will be returned. func (dec *Decoder) Int(v *int) error { err := dec.decodeInt(v) if err != nil { return err } dec.called |= 1 return nil } // IntNull decodes the JSON value within an object or an array to an *int. // If next key value overflows int, an InvalidUnmarshalError error will be returned. func (dec *Decoder) IntNull(v **int) error { err := dec.decodeIntNull(v) if err != nil { return err } dec.called |= 1 return nil } // Int8 decodes the JSON value within an object or an array to an *int. // If next key value overflows int8, an InvalidUnmarshalError error will be returned. func (dec *Decoder) Int8(v *int8) error { err := dec.decodeInt8(v) if err != nil { return err } dec.called |= 1 return nil } // Int8Null decodes the JSON value within an object or an array to an *int. // If next key value overflows int8, an InvalidUnmarshalError error will be returned. func (dec *Decoder) Int8Null(v **int8) error { err := dec.decodeInt8Null(v) if err != nil { return err } dec.called |= 1 return nil } // Int16 decodes the JSON value within an object or an array to an *int. // If next key value overflows int16, an InvalidUnmarshalError error will be returned. func (dec *Decoder) Int16(v *int16) error { err := dec.decodeInt16(v) if err != nil { return err } dec.called |= 1 return nil } // Int16Null decodes the JSON value within an object or an array to an *int. // If next key value overflows int16, an InvalidUnmarshalError error will be returned. func (dec *Decoder) Int16Null(v **int16) error { err := dec.decodeInt16Null(v) if err != nil { return err } dec.called |= 1 return nil } // Int32 decodes the JSON value within an object or an array to an *int. // If next key value overflows int32, an InvalidUnmarshalError error will be returned. func (dec *Decoder) Int32(v *int32) error { err := dec.decodeInt32(v) if err != nil { return err } dec.called |= 1 return nil } // Int32Null decodes the JSON value within an object or an array to an *int. // If next key value overflows int32, an InvalidUnmarshalError error will be returned. func (dec *Decoder) Int32Null(v **int32) error { err := dec.decodeInt32Null(v) if err != nil { return err } dec.called |= 1 return nil } // Int64 decodes the JSON value within an object or an array to an *int. // If next key value overflows int64, an InvalidUnmarshalError error will be returned. func (dec *Decoder) Int64(v *int64) error { err := dec.decodeInt64(v) if err != nil { return err } dec.called |= 1 return nil } // Int64Null decodes the JSON value within an object or an array to an *int. // If next key value overflows int64, an InvalidUnmarshalError error will be returned. func (dec *Decoder) Int64Null(v **int64) error { err := dec.decodeInt64Null(v) if err != nil { return err } dec.called |= 1 return nil } golang-github-francoispqt-gojay-1.2.13/decode_number_int_test.go000066400000000000000000002304351365331665100250360ustar00rootroot00000000000000package gojay import ( "fmt" "reflect" "strings" "testing" "github.com/stretchr/testify/assert" ) func TestDecoderInt(t *testing.T) { testCases := []struct { name string json string expectedResult int err bool errType interface{} }{ { name: "basic-positive", json: "100", expectedResult: 100, }, { name: "basic-positive2", json: "1039405", expectedResult: 1039405, }, { name: "basic-negative", json: "-2", expectedResult: -2, }, { name: "basic-null", json: "null", expectedResult: 0, }, { name: "basic-negative-err", json: "-", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "basic-negative-err", json: "-q", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "basic-null-err", json: "nxll", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "basic-skip-data-err", json: "trua", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "basic-big", json: "9223372036854775807", expectedResult: 9223372036854775807, }, { name: "basic-big-overflow", json: "9223372036854775808", expectedResult: 0, err: true, errType: InvalidUnmarshalError(""), }, { name: "basic-big-overflow2", json: "92233720368547758089", expectedResult: 0, err: true, errType: InvalidUnmarshalError(""), }, { name: "basic-big-overflow3", json: "92233720368547758089 ", expectedResult: 0, err: true, errType: InvalidUnmarshalError(""), }, { name: "basic-negative2", json: "-2349557", expectedResult: -2349557, }, { name: "exponent-err-too-big", json: "0e10000000000000000000", expectedResult: 0, err: true, }, { name: "basic-float", json: "2.4595", expectedResult: 2, }, { name: "basic-float2", json: "-7.8876", expectedResult: -7, }, { name: "basic-float2", json: "-7.8876 ", expectedResult: -7, }, { name: "basic-float2", json: "-7.8876a", expectedResult: 0, err: true, }, { name: "basic-exponent-positive-positive-exp", json: "1e2", expectedResult: 100, }, { name: "basic-exponent-positive-positive-exp2", json: "5e+06", expectedResult: 5000000, }, { name: "basic-exponent-positive-positive-exp2", json: "5.01e+10", expectedResult: 50100000000, }, { name: "basic-exponent-positive-positive-exp3", json: "3e+3", expectedResult: 3000, }, { name: "basic-exponent-positive-positive-exp4", json: "8e+005", expectedResult: 800000, }, { name: "basic-exponent-positive-negative-exp", json: "1e-2", expectedResult: 0, }, { name: "basic-exponent-positive-negative-exp2", json: "5e-6", expectedResult: 0, }, { name: "basic-exponent-positive-negative-exp3", json: "3e-3", expectedResult: 0, }, { name: "basic-exponent-positive-negative-exp4", json: "8e-005", expectedResult: 0, }, { name: "basic-exponent-negative-positive-exp", json: "-1e2", expectedResult: -100, }, { name: "basic-exponent-negative-positive-exp2", json: "-5e+06", expectedResult: -5000000, }, { name: "basic-exponent-negative-positive-exp3", json: "-3e03", expectedResult: -3000, }, { name: "basic-exponent-negative-positive-exp4", json: "-8e+005", expectedResult: -800000, }, { name: "error1", json: "132zz4", expectedResult: 0, err: true, }, { name: "negative-error2", json: " -1213xdde2323 ", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "error3", json: "-8e+00$aa5", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "error4", json: "0.E----", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "exponent-err-", json: "0.1e", expectedResult: 0, err: true, }, { name: "error5", json: "0E40", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "error6", json: "0.e-9", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "error7", json: "-5.e-2", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "invalid-type", json: `"string"`, expectedResult: 0, err: true, errType: InvalidUnmarshalError(""), }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { json := []byte(testCase.json) var v int err := Unmarshal(json, &v) if testCase.err { assert.NotNil(t, err, "Err must not be nil") if testCase.errType != nil && err != nil { assert.IsType( t, testCase.errType, err, fmt.Sprintf("err should be of type %s", reflect.TypeOf(err).String()), ) } } else { assert.Nil(t, err, "Err must be nil") } assert.Equal(t, testCase.expectedResult, v, fmt.Sprintf("v must be equal to %d", testCase.expectedResult)) }) } t.Run("pool-error", func(t *testing.T) { result := int(1) dec := NewDecoder(nil) dec.Release() defer func() { err := recover() assert.NotNil(t, err, "err shouldnt be nil") assert.IsType(t, InvalidUsagePooledDecoderError(""), err, "err should be of type InvalidUsagePooledDecoderError") }() _ = dec.DecodeInt(&result) assert.True(t, false, "should not be called as decoder should have panicked") }) t.Run("decoder-api", func(t *testing.T) { var v int dec := NewDecoder(strings.NewReader(`33`)) defer dec.Release() err := dec.DecodeInt(&v) assert.Nil(t, err, "Err must be nil") assert.Equal(t, int(33), v, "v must be equal to 33") }) t.Run("decoder-api2", func(t *testing.T) { var v int dec := NewDecoder(strings.NewReader(`33`)) defer dec.Release() err := dec.Decode(&v) assert.Nil(t, err, "Err must be nil") assert.Equal(t, int(33), v, "v must be equal to 33") }) t.Run("decoder-api-invalid-json", func(t *testing.T) { var v int dec := NewDecoder(strings.NewReader(``)) defer dec.Release() err := dec.DecodeInt(&v) assert.NotNil(t, err, "Err must not be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") }) } func TestDecoderIntNull(t *testing.T) { testCases := []struct { name string json string expectedResult int err bool errType interface{} resultIsNil bool }{ { name: "basic-positive", json: "100", expectedResult: 100, }, { name: "basic-positive2", json: "1039405", expectedResult: 1039405, }, { name: "basic-negative", json: "-2", expectedResult: -2, }, { name: "basic-null", json: "null", expectedResult: 0, resultIsNil: true, }, { name: "basic-negative-err", json: "-", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "basic-negative-err", json: "-q", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "basic-null-err", json: "nxll", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "basic-skip-data-err", json: "trua", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "basic-big", json: "9223372036854775807", expectedResult: 9223372036854775807, }, { name: "basic-big-overflow", json: "9223372036854775808", expectedResult: 0, err: true, errType: InvalidUnmarshalError(""), }, { name: "basic-big-overflow2", json: "92233720368547758089", expectedResult: 0, err: true, errType: InvalidUnmarshalError(""), }, { name: "basic-big-overflow3", json: "92233720368547758089 ", expectedResult: 0, err: true, errType: InvalidUnmarshalError(""), }, { name: "basic-negative2", json: "-2349557", expectedResult: -2349557, }, { name: "exponent-err-too-big", json: "0e10000000000000000000", expectedResult: 0, err: true, }, { name: "basic-float", json: "2.4595", expectedResult: 2, }, { name: "basic-float2", json: "-7.8876", expectedResult: -7, }, { name: "basic-float2", json: "-7.8876 ", expectedResult: -7, }, { name: "basic-float2", json: "-7.8876a", expectedResult: 0, err: true, }, { name: "basic-exponent-positive-positive-exp", json: "1e2", expectedResult: 100, }, { name: "basic-exponent-positive-positive-exp2", json: "5e+06", expectedResult: 5000000, }, { name: "basic-exponent-positive-positive-exp2", json: "5.01e+10", expectedResult: 50100000000, }, { name: "basic-exponent-positive-positive-exp3", json: "3e+3", expectedResult: 3000, }, { name: "basic-exponent-positive-positive-exp4", json: "8e+005", expectedResult: 800000, }, { name: "basic-exponent-positive-negative-exp", json: "1e-2", expectedResult: 0, }, { name: "basic-exponent-positive-negative-exp2", json: "5e-6", expectedResult: 0, }, { name: "basic-exponent-positive-negative-exp3", json: "3e-3", expectedResult: 0, }, { name: "basic-exponent-positive-negative-exp4", json: "8e-005", expectedResult: 0, }, { name: "basic-exponent-negative-positive-exp", json: "-1e2", expectedResult: -100, }, { name: "basic-exponent-negative-positive-exp2", json: "-5e+06", expectedResult: -5000000, }, { name: "basic-exponent-negative-positive-exp3", json: "-3e03", expectedResult: -3000, }, { name: "basic-exponent-negative-positive-exp4", json: "-8e+005", expectedResult: -800000, }, { name: "error1", json: "132zz4", expectedResult: 0, err: true, }, { name: "negative-error2", json: " -1213xdde2323 ", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "error3", json: "-8e+00$aa5", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "error4", json: "0.E----", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "exponent-err-", json: "0.1e", expectedResult: 0, err: true, }, { name: "error5", json: "0E40", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "error6", json: "0.e-9", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "error7", json: "-5.e-2", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "invalid-type", json: `"string"`, expectedResult: 0, err: true, errType: InvalidUnmarshalError(""), }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { json := []byte(testCase.json) var v = (*int)(nil) err := Unmarshal(json, &v) if testCase.err { assert.NotNil(t, err, "Err must not be nil") if testCase.errType != nil && err != nil { assert.IsType( t, testCase.errType, err, fmt.Sprintf("err should be of type %s", reflect.TypeOf(err).String()), ) } return } assert.Nil(t, err, "Err must be nil") if testCase.resultIsNil { assert.Nil(t, v) } else { assert.Equal(t, testCase.expectedResult, *v, fmt.Sprintf("v must be equal to %d", testCase.expectedResult)) } }) } t.Run("decoder-api-invalid-json", func(t *testing.T) { var v = new(int) err := Unmarshal([]byte(``), &v) assert.NotNil(t, err, "Err must not be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") }) t.Run("decoder-api-invalid-json2", func(t *testing.T) { var v = new(int) var dec = NewDecoder(strings.NewReader(``)) err := dec.IntNull(&v) assert.NotNil(t, err, "Err must not be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") }) } func TestDecoderInt64(t *testing.T) { testCases := []struct { name string json string expectedResult int64 err bool errType interface{} }{ { name: "basic-positive", json: "100", expectedResult: 100, }, { name: "basic-positive2", json: " 1039405", expectedResult: 1039405, }, { name: "basic-negative", json: "-2", expectedResult: -2, }, { name: "basic-null", json: "null", expectedResult: 0, }, { name: "basic-null-err", json: "nxll", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "basic-negative-err", json: "-", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "basic-negative-err", json: "-q", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "basic-skip-data-err", json: "trua", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "basic-big", json: "9223372036854775807", expectedResult: 9223372036854775807, }, { name: "basic-big-overflow", json: " 9223372036854775808", expectedResult: 0, err: true, }, { name: "basic-big-overflow", json: " 9223372036854775827", expectedResult: 0, err: true, }, { name: "basic-big-overflow2", json: "92233720368547758089", expectedResult: 0, err: true, }, { name: "basic-big-overflow3", json: "92233720368547758089 ", expectedResult: 0, err: true, }, { name: "basic-negative2", json: "-2349557", expectedResult: -2349557, }, { name: "basic-float", json: "2.4595", expectedResult: 2, }, { name: "basic-float2", json: "-7.8876", expectedResult: -7, }, { name: "basic-float2", json: "-7.8876a", expectedResult: 0, err: true, }, { name: "basic-exponent-positive-positive-exp", json: "1e2", expectedResult: 100, }, { name: "basic-exponent-positive-positive-exp2", json: "5e+06 ", expectedResult: 5000000, }, { name: "basic-exponent-positive-positive-exp3", json: "3e+3", expectedResult: 3000, }, { name: "basic-exponent-positive-positive-exp4", json: "8e+005", expectedResult: 800000, }, { name: "basic-exponent-positive-negative-exp", json: "1e-2 ", expectedResult: 0, }, { name: "basic-exponent-positive-negative-exp2", json: "5e-6", expectedResult: 0, }, { name: "basic-exponent-positive-negative-exp3", json: "3e-3", expectedResult: 0, }, { name: "before-exp-err-too-big", json: "10.11231242345325435464364643e1", expectedResult: 0, err: true, }, { name: "error3", json: "0E40", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "basic-exponent-positive-negative-exp4", json: "8e-005", expectedResult: 0, }, { name: "basic-exponent-negative-positive-exp", json: "-1e2", expectedResult: -100, }, { name: "basic-exponent-negative-positive-exp2", json: "-5e+06", expectedResult: -5000000, }, { name: "basic-exponent-negative-positive-exp2", json: "-5.4e+06", expectedResult: -5400000, }, { name: "basic-exponent-negative-positive-exp3", json: "-3e03", expectedResult: -3000, }, { name: "basic-exponent-negative-positive-exp4", json: "-8e+005", expectedResult: -800000, }, { name: "exponent-err-too-big", json: "0e10000000000000000000", expectedResult: 0, err: true, }, { name: "exponent-err-too-big", json: "0e1000000000000000000000000 ", expectedResult: 0, err: true, }, { name: "exponent-err-too-big", json: "0.1e1000000000", expectedResult: 0, err: true, }, { name: "exponent-err-too-big", json: "0.1932242242424244244e1000000000000000000000000", expectedResult: 0, err: true, }, { name: "basic-exponent-negative-positive-exp4", json: "8ea+00a5", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "basic-exponent-err", json: "3e", expectedResult: 0, err: true, }, { name: "error1", json: "132zz4", expectedResult: 0, err: true, }, { name: "error4", json: "0.E----", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "error5", json: "0E40", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "exponent-err-", json: "0.1e", expectedResult: 0, err: true, }, { name: "error6", json: "0.e-9", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "error7", json: "-5.e-2", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "invalid-type", json: `"string"`, expectedResult: 0, err: true, errType: InvalidUnmarshalError(""), }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { json := []byte(testCase.json) var v int64 err := Unmarshal(json, &v) if testCase.err { assert.NotNil(t, err, "Err must not be nil") if testCase.errType != nil { assert.IsType( t, testCase.errType, err, fmt.Sprintf("err should be of type %s", reflect.TypeOf(err).String()), ) } } else { assert.Nil(t, err, "Err must be nil") } assert.Equal(t, testCase.expectedResult, v, fmt.Sprintf("v must be equal to %d", testCase.expectedResult)) }) } t.Run("pool-error", func(t *testing.T) { result := int64(1) dec := NewDecoder(nil) dec.Release() defer func() { err := recover() assert.NotNil(t, err, "err shouldnt be nil") assert.IsType(t, InvalidUsagePooledDecoderError(""), err, "err should be of type InvalidUsagePooledDecoderError") }() _ = dec.DecodeInt64(&result) assert.True(t, false, "should not be called as decoder should have panicked") }) t.Run("decoder-api", func(t *testing.T) { var v int64 dec := NewDecoder(strings.NewReader(`33`)) defer dec.Release() err := dec.DecodeInt64(&v) assert.Nil(t, err, "Err must be nil") assert.Equal(t, int64(33), v, "v must be equal to 33") }) t.Run("decoder-api2", func(t *testing.T) { var v int64 dec := NewDecoder(strings.NewReader(`33`)) defer dec.Release() err := dec.Decode(&v) assert.Nil(t, err, "Err must be nil") assert.Equal(t, int64(33), v, "v must be equal to 33") }) t.Run("decoder-api-invalid-json", func(t *testing.T) { var v int64 dec := NewDecoder(strings.NewReader(``)) defer dec.Release() err := dec.DecodeInt64(&v) assert.NotNil(t, err, "Err must not be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") }) } func TestDecoderInt64Null(t *testing.T) { testCases := []struct { name string json string expectedResult int64 err bool errType interface{} resultIsNil bool }{ { name: "basic-positive", json: "100", expectedResult: 100, }, { name: "basic-positive2", json: " 1039405", expectedResult: 1039405, }, { name: "basic-negative", json: "-2", expectedResult: -2, }, { name: "basic-null", json: "null", expectedResult: 0, resultIsNil: true, }, { name: "basic-null-err", json: "nxll", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "basic-negative-err", json: "-", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "basic-negative-err", json: "-q", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "basic-skip-data-err", json: "trua", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "basic-big", json: "9223372036854775807", expectedResult: 9223372036854775807, }, { name: "basic-big-overflow", json: " 9223372036854775808", expectedResult: 0, err: true, }, { name: "basic-big-overflow", json: " 9223372036854775827", expectedResult: 0, err: true, }, { name: "basic-big-overflow2", json: "92233720368547758089", expectedResult: 0, err: true, }, { name: "basic-big-overflow3", json: "92233720368547758089 ", expectedResult: 0, err: true, }, { name: "basic-negative2", json: "-2349557", expectedResult: -2349557, }, { name: "basic-float", json: "2.4595", expectedResult: 2, }, { name: "basic-float2", json: "-7.8876", expectedResult: -7, }, { name: "basic-float2", json: "-7.8876a", expectedResult: 0, err: true, }, { name: "basic-exponent-positive-positive-exp", json: "1e2", expectedResult: 100, }, { name: "basic-exponent-positive-positive-exp2", json: "5e+06 ", expectedResult: 5000000, }, { name: "basic-exponent-positive-positive-exp3", json: "3e+3", expectedResult: 3000, }, { name: "basic-exponent-positive-positive-exp4", json: "8e+005", expectedResult: 800000, }, { name: "basic-exponent-positive-negative-exp", json: "1e-2 ", expectedResult: 0, }, { name: "basic-exponent-positive-negative-exp2", json: "5e-6", expectedResult: 0, }, { name: "basic-exponent-positive-negative-exp3", json: "3e-3", expectedResult: 0, }, { name: "error3", json: "0E40", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "basic-exponent-positive-negative-exp4", json: "8e-005", expectedResult: 0, }, { name: "basic-exponent-negative-positive-exp", json: "-1e2", expectedResult: -100, }, { name: "basic-exponent-negative-positive-exp2", json: "-5e+06", expectedResult: -5000000, }, { name: "basic-exponent-negative-positive-exp2", json: "-5.4e+06", expectedResult: -5400000, }, { name: "basic-exponent-negative-positive-exp3", json: "-3e03", expectedResult: -3000, }, { name: "basic-exponent-negative-positive-exp4", json: "-8e+005", expectedResult: -800000, }, { name: "exponent-err-too-big", json: "0e10000000000000000000", expectedResult: 0, err: true, }, { name: "exponent-err-too-big", json: "0e1000000000000000000000000 ", expectedResult: 0, err: true, }, { name: "exponent-err-too-big", json: "0.1e1000000000", expectedResult: 0, err: true, }, { name: "exponent-err-too-big", json: "0.1932242242424244244e1000000000000000000000000", expectedResult: 0, err: true, }, { name: "basic-exponent-negative-positive-exp4", json: "8ea+00a5", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "basic-exponent-err", json: "3e", expectedResult: 0, err: true, }, { name: "error1", json: "132zz4", expectedResult: 0, err: true, }, { name: "error4", json: "0.E----", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "error5", json: "0E40", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "exponent-err-", json: "0.1e", expectedResult: 0, err: true, }, { name: "error6", json: "0.e-9", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "error7", json: "-5.e-2", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "invalid-type", json: `"string"`, expectedResult: 0, err: true, errType: InvalidUnmarshalError(""), }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { json := []byte(testCase.json) var v = (*int64)(nil) err := Unmarshal(json, &v) if testCase.err { assert.NotNil(t, err, "Err must not be nil") if testCase.errType != nil { assert.IsType( t, testCase.errType, err, fmt.Sprintf("err should be of type %s", reflect.TypeOf(err).String()), ) } return } assert.Nil(t, err, "Err must be nil") if testCase.resultIsNil { assert.Nil(t, v) } else { assert.Equal(t, testCase.expectedResult, *v, fmt.Sprintf("v must be equal to %d", testCase.expectedResult)) } }) } t.Run("decoder-api-invalid-json", func(t *testing.T) { var v = new(int64) err := Unmarshal([]byte(``), &v) assert.NotNil(t, err, "Err must not be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") }) t.Run("decoder-api-invalid-json2", func(t *testing.T) { var v = new(int64) var dec = NewDecoder(strings.NewReader(``)) err := dec.Int64Null(&v) assert.NotNil(t, err, "Err must not be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") }) } func TestDecoderInt32(t *testing.T) { testCases := []struct { name string json string expectedResult int32 err bool errType interface{} }{ { name: "basic-positive", json: "100", expectedResult: 100, }, { name: "basic-positive2", json: " 1039405", expectedResult: 1039405, }, { name: "basic-negative", json: "-2", expectedResult: -2, }, { name: "basic-null", json: "null", expectedResult: 0, }, { name: "basic-null-err", json: "nxll", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "basic-negative-err", json: "-", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "basic-negative-err", json: "-q", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "basic-skip-data-err", json: "trua", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "basic-negative2", json: "-2349557", expectedResult: -2349557, }, { name: "basic-big", json: " 2147483647", expectedResult: 2147483647, }, { name: "basic-big-overflow", json: " 2147483648", expectedResult: 0, err: true, }, { name: "basic-big-overflow", json: " 2147483657", expectedResult: 0, err: true, }, { name: "basic-big-overflow2", json: "21474836483", expectedResult: 0, err: true, }, { name: "basic-float", json: "2.4595", expectedResult: 2, }, { name: "basic-float2", json: "-7.8876", expectedResult: -7, }, { name: "basic-float2", json: "-7.8876a", expectedResult: 0, err: true, }, { name: "basic-exponent-positive-positive-exp", json: "1.2E2", expectedResult: 120, }, { name: "exponent-err-too-big", json: "0e10000000000000000000", expectedResult: 0, err: true, }, { name: "exponent-err-too-big", json: "0.1932242242424244244e1000000000000000000000000", expectedResult: 0, err: true, }, { name: "basic-exponent-positive-positive-exp1", json: "3.5e+005 ", expectedResult: 350000, }, { name: "basic-exponent-positive-positive-exp1", json: "3.5e+005", expectedResult: 350000, }, { name: "basic-exponent-positive-positive-exp2", json: "5e+06", expectedResult: 5000000, }, { name: "basic-exponent-positive-positive-exp3", json: "3e+3", expectedResult: 3000, }, { name: "basic-exponent-positive-positive-exp4", json: "8e+005 ", expectedResult: 800000, }, { name: "basic-exponent-positive-negative-exp", json: "1e-2 ", expectedResult: 0, }, { name: "basic-exponent-positive-negative-exp2", json: "5E-6", expectedResult: 0, }, { name: "basic-exponent-positive-negative-exp3", json: "3e-3", expectedResult: 0, }, { name: "basic-exponent-positive-negative-exp4", json: "8e-005", expectedResult: 0, }, { name: "basic-exponent-negative-positive-exp", json: "-1e2", expectedResult: -100, }, { name: "basic-exponent-negative-positive-exp2", json: "-5e+06", expectedResult: -5000000, }, { name: "basic-exponent-negative-positive-exp3", json: "-3e03", expectedResult: -3000, }, { name: "basic-exponent-negative-positive-exp4", json: "-8e+005", expectedResult: -800000, }, { name: "before-exp-err-too-big", json: "10.11231242345325435464364643e1", expectedResult: 0, err: true, }, { name: "exponent-err-", json: "0.1e", expectedResult: 0, err: true, }, { name: "exponent-err-too-big", json: "0.1e10000000000000000000", expectedResult: 0, err: true, }, { name: "exponent-err-too-big", json: "0.1e1000000000", expectedResult: 0, err: true, }, { name: "exponent-err-too-big", json: "0.1e1000000000 ", expectedResult: 0, err: true, }, { name: "exponent-err-too-big", json: "0e100000000000", expectedResult: 0, err: true, }, { name: "exponent-err-too-big", json: "0e100000000000 ", expectedResult: 0, err: true, }, { name: "basic-exponent-err", json: "3e", expectedResult: 0, err: true, }, { name: "error3", json: "0E40", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "error4", json: "0.E----", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "error5", json: "0E40", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "error6", json: "0.e-9", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "error7", json: "-5.e-2", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "basic-float", json: "8.32 ", expectedResult: 8, }, { name: "error", json: "83zez4", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "error", json: "8ea00$aa5", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "error2", json: "-8e+00$aa5", expectedResult: 0, err: true, }, { name: "invalid-type", json: `"string"`, expectedResult: 0, err: true, errType: InvalidUnmarshalError(""), }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { json := []byte(testCase.json) var v int32 err := Unmarshal(json, &v) if testCase.err { assert.NotNil(t, err, "Err must not be nil") if testCase.errType != nil { assert.IsType( t, testCase.errType, err, fmt.Sprintf("err should be of type %s", reflect.TypeOf(err).String()), ) } } else { assert.Nil(t, err, "Err must be nil") } assert.Equal(t, testCase.expectedResult, v, fmt.Sprintf("v must be equal to %d", testCase.expectedResult)) }) } t.Run("pool-error", func(t *testing.T) { result := int32(1) dec := NewDecoder(nil) dec.Release() defer func() { err := recover() assert.NotNil(t, err, "err shouldnt be nil") assert.IsType(t, InvalidUsagePooledDecoderError(""), err, "err should be of type InvalidUsagePooledDecoderError") }() _ = dec.DecodeInt32(&result) assert.True(t, false, "should not be called as decoder should have panicked") }) t.Run("decoder-api", func(t *testing.T) { var v int32 dec := NewDecoder(strings.NewReader(`33`)) defer dec.Release() err := dec.DecodeInt32(&v) assert.Nil(t, err, "Err must be nil") assert.Equal(t, int32(33), v, "v must be equal to 33") }) t.Run("decoder-api2", func(t *testing.T) { var v int32 dec := NewDecoder(strings.NewReader(`33`)) defer dec.Release() err := dec.Decode(&v) assert.Nil(t, err, "Err must be nil") assert.Equal(t, int32(33), v, "v must be equal to 33") }) t.Run("decoder-api-invalid-json", func(t *testing.T) { var v int32 dec := NewDecoder(strings.NewReader(``)) defer dec.Release() err := dec.DecodeInt32(&v) assert.NotNil(t, err, "Err must not be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") }) } func TestDecoderInt32Null(t *testing.T) { testCases := []struct { name string json string expectedResult int32 err bool errType interface{} resultIsNil bool }{ { name: "basic-positive", json: "100", expectedResult: 100, }, { name: "basic-positive2", json: " 1039405", expectedResult: 1039405, }, { name: "basic-negative", json: "-2", expectedResult: -2, }, { name: "basic-null", json: "null", expectedResult: 0, resultIsNil: true, }, { name: "basic-null-err", json: "nxll", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "basic-negative-err", json: "-", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "basic-negative-err", json: "-q", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "basic-skip-data-err", json: "trua", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "basic-negative2", json: "-2349557", expectedResult: -2349557, }, { name: "basic-big", json: " 2147483647", expectedResult: 2147483647, }, { name: "basic-big-overflow", json: " 2147483648", expectedResult: 0, err: true, }, { name: "basic-big-overflow", json: " 2147483657", expectedResult: 0, err: true, }, { name: "basic-big-overflow2", json: "21474836483", expectedResult: 0, err: true, }, { name: "basic-float", json: "2.4595", expectedResult: 2, }, { name: "basic-float2", json: "-7.8876", expectedResult: -7, }, { name: "basic-float2", json: "-7.8876a", expectedResult: 0, err: true, }, { name: "basic-exponent-positive-positive-exp", json: "1.2E2", expectedResult: 120, }, { name: "exponent-err-too-big", json: "0e10000000000000000000", expectedResult: 0, err: true, }, { name: "exponent-err-too-big", json: "0.1932242242424244244e1000000000000000000000000", expectedResult: 0, err: true, }, { name: "basic-exponent-positive-positive-exp1", json: "3.5e+005 ", expectedResult: 350000, }, { name: "basic-exponent-positive-positive-exp1", json: "3.5e+005", expectedResult: 350000, }, { name: "basic-exponent-positive-positive-exp2", json: "5e+06", expectedResult: 5000000, }, { name: "basic-exponent-positive-positive-exp3", json: "3e+3", expectedResult: 3000, }, { name: "basic-exponent-positive-positive-exp4", json: "8e+005 ", expectedResult: 800000, }, { name: "basic-exponent-positive-negative-exp", json: "1e-2 ", expectedResult: 0, }, { name: "basic-exponent-positive-negative-exp2", json: "5E-6", expectedResult: 0, }, { name: "basic-exponent-positive-negative-exp3", json: "3e-3", expectedResult: 0, }, { name: "basic-exponent-positive-negative-exp4", json: "8e-005", expectedResult: 0, }, { name: "basic-exponent-negative-positive-exp", json: "-1e2", expectedResult: -100, }, { name: "basic-exponent-negative-positive-exp2", json: "-5e+06", expectedResult: -5000000, }, { name: "basic-exponent-negative-positive-exp3", json: "-3e03", expectedResult: -3000, }, { name: "basic-exponent-negative-positive-exp4", json: "-8e+005", expectedResult: -800000, }, { name: "exponent-err-", json: "0.1e", expectedResult: 0, err: true, }, { name: "exponent-err-too-big", json: "0.1e10000000000000000000", expectedResult: 0, err: true, }, { name: "exponent-err-too-big", json: "0.1e1000000000", expectedResult: 0, err: true, }, { name: "exponent-err-too-big", json: "0.1e1000000000 ", expectedResult: 0, err: true, }, { name: "exponent-err-too-big", json: "0e100000000000", expectedResult: 0, err: true, }, { name: "exponent-err-too-big", json: "0e100000000000 ", expectedResult: 0, err: true, }, { name: "basic-exponent-err", json: "3e", expectedResult: 0, err: true, }, { name: "error3", json: "0E40", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "error4", json: "0.E----", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "error5", json: "0E40", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "error6", json: "0.e-9", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "error7", json: "-5.e-2", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "basic-float", json: "8.32 ", expectedResult: 8, }, { name: "error", json: "83zez4", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "error", json: "8ea00$aa5", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "error2", json: "-8e+00$aa5", expectedResult: 0, err: true, }, { name: "invalid-type", json: `"string"`, expectedResult: 0, err: true, errType: InvalidUnmarshalError(""), }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { json := []byte(testCase.json) var v = (*int32)(nil) err := Unmarshal(json, &v) if testCase.err { assert.NotNil(t, err, "Err must not be nil") if testCase.errType != nil { assert.IsType( t, testCase.errType, err, fmt.Sprintf("err should be of type %s", reflect.TypeOf(err).String()), ) } return } assert.Nil(t, err, "Err must be nil") if testCase.resultIsNil { assert.Nil(t, v) } else { assert.Equal(t, testCase.expectedResult, *v, fmt.Sprintf("v must be equal to %d", testCase.expectedResult)) } }) } t.Run("decoder-api-invalid-json", func(t *testing.T) { var v = new(int32) err := Unmarshal([]byte(``), &v) assert.NotNil(t, err, "Err must not be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") }) t.Run("decoder-api-invalid-json2", func(t *testing.T) { var v = new(int32) var dec = NewDecoder(strings.NewReader(``)) err := dec.Int32Null(&v) assert.NotNil(t, err, "Err must not be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") }) } func TestDecoderInt16(t *testing.T) { testCases := []struct { name string json string expectedResult int16 err bool errType interface{} }{ { name: "basic-positive", json: "100", expectedResult: 100, }, { name: "basic-positive2", json: " 5321", expectedResult: 5321, }, { name: "basic-negative", json: "-2", expectedResult: -2, }, { name: "basic-null", json: "null", expectedResult: 0, }, { name: "basic-null-err", json: "nxll", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "basic-negative-err", json: "-", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "basic-negative-err", json: "-q", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "basic-skip-data-err", json: "trua", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "basic-negative2", json: "-2456", expectedResult: -2456, }, { name: "basic-big", json: " 24566", expectedResult: 24566, }, { name: "basic-big-overflow", json: "66535", expectedResult: 0, err: true, }, { name: "basic-big-overflow", json: "32768", expectedResult: 0, err: true, }, { name: "basic-big-overflow", json: " 2147483648", expectedResult: 0, err: true, }, { name: "basic-big-overflow2", json: "21474836483", expectedResult: 0, err: true, }, { name: "basic-float", json: "2.4595", expectedResult: 2, }, { name: "basic-float2", json: "-7.8876", expectedResult: -7, }, { name: "basic-float2", json: "-7.8876a", expectedResult: 0, err: true, }, { name: "basic-exponent-positive-positive-exp", json: "1.2E2", expectedResult: 120, }, { name: "exponent too big", json: "1000.202302302422324435342E2", err: true, }, { name: "basic-exponent-positive-positive-exp1", json: "3.5e+001 ", expectedResult: 35, }, { name: "basic-exponent-positive-positive-exp1", json: "3.5e+002", expectedResult: 350, }, { name: "basic-exponent-positive-positive-exp2", json: "5e+03", expectedResult: 5000, }, { name: "basic-exponent-positive-positive-exp3", json: "3e+3", expectedResult: 3000, }, { name: "basic-exponent-positive-positive-exp4", json: "8e+02 ", expectedResult: 800, }, { name: "basic-exponent-positive-negative-exp", json: "1e-2 ", expectedResult: 0, }, { name: "basic-exponent-positive-negative-exp2", json: "5E-6", expectedResult: 0, }, { name: "basic-exponent-positive-negative-exp3", json: "3e-3", expectedResult: 0, }, { name: "basic-exponent-positive-negative-exp4", json: "8e-005", expectedResult: 0, }, { name: "basic-exponent-negative-positive-exp", json: "-1e2", expectedResult: -100, }, { name: "exponent-err-", json: "0.1e", expectedResult: 0, err: true, }, { name: "exponent-err-too-big", json: "0.1e10000000000000000000", expectedResult: 0, err: true, }, { name: "exponent-err-too-big", json: "0.1e10000000000 ", expectedResult: 0, err: true, }, { name: "exponent-err-too-big", json: "0e10000000000 ", expectedResult: 0, err: true, }, { name: "exponent-err-too-big", json: "0.1932242242424244244e1000000000000000000000000", expectedResult: 0, err: true, }, { name: "basic-exponent-negative-positive-exp2", json: "-5e+03", expectedResult: -5000, }, { name: "basic-exponent-negative-positive-exp3", json: "-3e03", expectedResult: -3000, }, { name: "basic-exponent-negative-positive-exp4", json: "-8e+003", expectedResult: -8000, }, { name: "basic-exponent-err", json: "3e", expectedResult: 0, err: true, }, { name: "basic-float", json: "8.32 ", expectedResult: 8, }, { name: "error", json: "83zez4", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "error", json: "8ea00$aa5", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "error2", json: "-8e+00$aa5", expectedResult: 0, err: true, }, { name: "error3", json: "0E40", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "error4", json: "0.E----", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "error5", json: "0E40", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "error6", json: "0.e-9", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "error7", json: "0.e", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "error8", json: "-5.e-2", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "invalid-type", json: `"string"`, expectedResult: 0, err: true, errType: InvalidUnmarshalError(""), }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { json := []byte(testCase.json) var v int16 err := Unmarshal(json, &v) if testCase.err { assert.NotNil(t, err, "Err must not be nil") if testCase.errType != nil { assert.IsType( t, testCase.errType, err, fmt.Sprintf("err should be of type %s", reflect.TypeOf(err).String()), ) } } else { assert.Nil(t, err, "Err must be nil") } assert.Equal(t, testCase.expectedResult, v, fmt.Sprintf("v must be equal to %d", testCase.expectedResult)) }) } t.Run("pool-error", func(t *testing.T) { result := int16(1) dec := NewDecoder(nil) dec.Release() defer func() { err := recover() assert.NotNil(t, err, "err shouldnt be nil") assert.IsType(t, InvalidUsagePooledDecoderError(""), err, "err should be of type InvalidUsagePooledDecoderError") }() _ = dec.DecodeInt16(&result) assert.True(t, false, "should not be called as decoder should have panicked") }) t.Run("decoder-api", func(t *testing.T) { var v int16 dec := NewDecoder(strings.NewReader(`33`)) defer dec.Release() err := dec.DecodeInt16(&v) assert.Nil(t, err, "Err must be nil") assert.Equal(t, int16(33), v, "v must be equal to 33") }) t.Run("decoder-api2", func(t *testing.T) { var v int16 dec := NewDecoder(strings.NewReader(`33`)) defer dec.Release() err := dec.Decode(&v) assert.Nil(t, err, "Err must be nil") assert.Equal(t, int16(33), v, "v must be equal to 33") }) t.Run("decoder-api-invalid-json", func(t *testing.T) { var v int16 dec := NewDecoder(strings.NewReader(``)) defer dec.Release() err := dec.DecodeInt16(&v) assert.NotNil(t, err, "Err must not be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") }) } func TestDecoderInt16Null(t *testing.T) { testCases := []struct { name string json string expectedResult int16 err bool errType interface{} resultIsNil bool }{ { name: "basic-positive", json: "100", expectedResult: 100, }, { name: "basic-positive2", json: " 5321", expectedResult: 5321, }, { name: "basic-negative", json: "-2", expectedResult: -2, }, { name: "basic-null", json: "null", expectedResult: 0, resultIsNil: true, }, { name: "basic-null-err", json: "nxll", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "basic-negative-err", json: "-", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "basic-negative-err", json: "-q", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "basic-skip-data-err", json: "trua", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "basic-negative2", json: "-2456", expectedResult: -2456, }, { name: "basic-big", json: " 24566", expectedResult: 24566, }, { name: "basic-big-overflow", json: "66535", expectedResult: 0, err: true, }, { name: "basic-big-overflow", json: "32768", expectedResult: 0, err: true, }, { name: "basic-big-overflow", json: " 2147483648", expectedResult: 0, err: true, }, { name: "basic-big-overflow2", json: "21474836483", expectedResult: 0, err: true, }, { name: "basic-float", json: "2.4595", expectedResult: 2, }, { name: "basic-float2", json: "-7.8876", expectedResult: -7, }, { name: "basic-float2", json: "-7.8876a", expectedResult: 0, err: true, }, { name: "basic-exponent-positive-positive-exp", json: "1.2E2", expectedResult: 120, }, { name: "basic-exponent-positive-positive-exp1", json: "3.5e+001 ", expectedResult: 35, }, { name: "basic-exponent-positive-positive-exp1", json: "3.5e+002", expectedResult: 350, }, { name: "basic-exponent-positive-positive-exp2", json: "5e+03", expectedResult: 5000, }, { name: "basic-exponent-positive-positive-exp3", json: "3e+3", expectedResult: 3000, }, { name: "basic-exponent-positive-positive-exp4", json: "8e+02 ", expectedResult: 800, }, { name: "basic-exponent-positive-negative-exp", json: "1e-2 ", expectedResult: 0, }, { name: "basic-exponent-positive-negative-exp2", json: "5E-6", expectedResult: 0, }, { name: "basic-exponent-positive-negative-exp3", json: "3e-3", expectedResult: 0, }, { name: "basic-exponent-positive-negative-exp4", json: "8e-005", expectedResult: 0, }, { name: "basic-exponent-negative-positive-exp", json: "-1e2", expectedResult: -100, }, { name: "exponent-err-", json: "0.1e", expectedResult: 0, err: true, }, { name: "exponent-err-too-big", json: "0.1e10000000000000000000", expectedResult: 0, err: true, }, { name: "exponent-err-too-big", json: "0.1e10000000000 ", expectedResult: 0, err: true, }, { name: "exponent-err-too-big", json: "0e10000000000 ", expectedResult: 0, err: true, }, { name: "exponent-err-too-big", json: "0.1932242242424244244e1000000000000000000000000", expectedResult: 0, err: true, }, { name: "basic-exponent-negative-positive-exp2", json: "-5e+03", expectedResult: -5000, }, { name: "basic-exponent-negative-positive-exp3", json: "-3e03", expectedResult: -3000, }, { name: "basic-exponent-negative-positive-exp4", json: "-8e+003", expectedResult: -8000, }, { name: "basic-exponent-err", json: "3e", expectedResult: 0, err: true, }, { name: "basic-float", json: "8.32 ", expectedResult: 8, }, { name: "error", json: "83zez4", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "error", json: "8ea00$aa5", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "error2", json: "-8e+00$aa5", expectedResult: 0, err: true, }, { name: "error3", json: "0E40", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "error4", json: "0.E----", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "error5", json: "0E40", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "error6", json: "0.e-9", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "error7", json: "0.e", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "error8", json: "-5.e-2", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "invalid-type", json: `"string"`, expectedResult: 0, err: true, errType: InvalidUnmarshalError(""), }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { json := []byte(testCase.json) var v = (*int16)(nil) err := Unmarshal(json, &v) if testCase.err { assert.NotNil(t, err, "Err must not be nil") if testCase.errType != nil { assert.IsType( t, testCase.errType, err, fmt.Sprintf("err should be of type %s", reflect.TypeOf(err).String()), ) } return } assert.Nil(t, err, "Err must be nil") if testCase.resultIsNil { assert.Nil(t, v) } else { assert.Equal(t, testCase.expectedResult, *v, fmt.Sprintf("v must be equal to %d", testCase.expectedResult)) } }) } t.Run("decoder-api-invalid-json", func(t *testing.T) { var v = new(int16) err := Unmarshal([]byte(``), &v) assert.NotNil(t, err, "Err must not be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") }) t.Run("decoder-api-invalid-json2", func(t *testing.T) { var v = new(int16) var dec = NewDecoder(strings.NewReader(``)) err := dec.Int16Null(&v) assert.NotNil(t, err, "Err must not be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") }) } func TestDecoderInt8(t *testing.T) { testCases := []struct { name string json string expectedResult int8 err bool errType interface{} }{ { name: "basic-positive", json: "100", expectedResult: 100, }, { name: "basic-positive2", json: " 127", expectedResult: 127, }, { name: "basic-negative", json: "-2", expectedResult: -2, }, { name: "basic-null", json: "null", expectedResult: 0, }, { name: "basic-negative-err", json: "-", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "basic-negative-err", json: "-q", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "basic-null-err", json: "nxll", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "basic-skip-data-err", json: "trua", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "basic-negative2", json: "-123", expectedResult: -123, }, { name: "basic-big", json: " 43", expectedResult: 43, }, { name: "basic-big-overflow", json: " 2147483648", expectedResult: 0, err: true, }, { name: "basic-big-overflow", json: "137", expectedResult: 0, err: true, }, { name: "basic-big-overflow", json: "128", expectedResult: 0, err: true, }, { name: "basic-big-overflow2", json: "21474836483", expectedResult: 0, err: true, }, { name: "basic-float", json: "2.4595", expectedResult: 2, }, { name: "basic-float2", json: "-7.8876", expectedResult: -7, }, { name: "basic-float2", json: "-7.8876a", expectedResult: 0, err: true, }, { name: "basic-exponent-positive-positive-exp", json: "1.2E2", expectedResult: 120, }, { name: "basic-exponent-positive-positive-exp1", json: "3.5e+001 ", expectedResult: 35, }, { name: "basic-exponent-positive-positive-exp1", json: "3.5e+001", expectedResult: 35, }, { name: "basic-exponent-positive-positive-exp2", json: "5e+01", expectedResult: 50, }, { name: "basic-exponent-positive-negative-exp", json: "1e-2 ", expectedResult: 0, }, { name: "basic-exponent-positive-negative-exp2", json: "5E-6", expectedResult: 0, }, { name: "basic-exponent-positive-negative-exp3", json: "3e-3", expectedResult: 0, }, { name: "basic-exponent-positive-negative-exp4", json: "8e-1 ", expectedResult: 0, }, { name: "basic-exponent-positive-negative-exp4", json: "8e1 ", expectedResult: 80, }, { name: "basic-exponent-positive-negative-exp4", json: "8e-1", expectedResult: 0, }, { name: "basic-exponent-negative-positive-exp", json: "-1e2", expectedResult: -100, }, { name: "basic-exponent-negative-positive-exp2", json: "-5e+01", expectedResult: -50, }, { name: "basic-exponent-negative-positive-exp3", json: "-3e01", expectedResult: -30, }, { name: "error3", json: "0E40", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "exponent-err-", json: "0.1e", expectedResult: 0, err: true, }, { name: "before-exp-err-too-big", json: "10.11231242345325435464364643e1", expectedResult: 0, err: true, }, { name: "exponent-err-too-big", json: "0.1e10000000000000000000", expectedResult: 0, err: true, }, { name: "exponent-err-too-big", json: "0.1932242242424244244e1000000000000000000000000", expectedResult: 0, err: true, }, { name: "basic-exponent-negative-positive-exp4", json: "-8e+001", expectedResult: -80, }, { name: "exponent-err-too-big2", json: "0e100 ", expectedResult: 0, err: true, }, { name: "exponent-err-too-big2", json: "0.1e100 ", expectedResult: 0, err: true, }, { name: "basic-exponent-err", json: "3e", expectedResult: 0, err: true, }, { name: "basic-float", json: "8.32 ", expectedResult: 8, }, { name: "error", json: "83zez4", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "error", json: "8ea00$aa5", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "error2", json: "-8e+00$aa5", expectedResult: 0, err: true, }, { name: "error4", json: "0.E----", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "error5", json: "0E40", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "error6", json: "0.e-9", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "error7", json: "0.e", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "error8", json: "-5.e-2", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "error8", json: "-5.01e", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "invalid-type", json: `"string"`, expectedResult: 0, err: true, errType: InvalidUnmarshalError(""), }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { json := []byte(testCase.json) var v int8 err := Unmarshal(json, &v) if testCase.err { assert.NotNil(t, err, "Err must not be nil") if testCase.errType != nil { assert.IsType( t, testCase.errType, err, fmt.Sprintf("err should be of type %s", reflect.TypeOf(err).String()), ) } } else { assert.Nil(t, err, "Err must be nil") } assert.Equal(t, testCase.expectedResult, v, fmt.Sprintf("v must be equal to %d", testCase.expectedResult)) }) } t.Run("pool-error", func(t *testing.T) { result := int8(1) dec := NewDecoder(nil) dec.Release() defer func() { err := recover() assert.NotNil(t, err, "err shouldnt be nil") assert.IsType(t, InvalidUsagePooledDecoderError(""), err, "err should be of type InvalidUsagePooledDecoderError") }() _ = dec.DecodeInt8(&result) assert.True(t, false, "should not be called as decoder should have panicked") }) t.Run("decoder-api", func(t *testing.T) { var v int8 dec := NewDecoder(strings.NewReader(`33`)) defer dec.Release() err := dec.DecodeInt8(&v) assert.Nil(t, err, "Err must be nil") assert.Equal(t, int8(33), v, "v must be equal to 33") }) t.Run("decoder-api2", func(t *testing.T) { var v int8 dec := NewDecoder(strings.NewReader(`33`)) defer dec.Release() err := dec.Decode(&v) assert.Nil(t, err, "Err must be nil") assert.Equal(t, int8(33), v, "v must be equal to 33") }) t.Run("decoder-api-invalid-json", func(t *testing.T) { var v int8 dec := NewDecoder(strings.NewReader(``)) defer dec.Release() err := dec.DecodeInt8(&v) assert.NotNil(t, err, "Err must not be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") }) } func TestDecoderInt8Null(t *testing.T) { testCases := []struct { name string json string expectedResult int8 err bool errType interface{} resultIsNil bool }{ { name: "basic-positive", json: "100", expectedResult: 100, }, { name: "basic-positive2", json: " 127", expectedResult: 127, }, { name: "basic-negative", json: "-2", expectedResult: -2, }, { name: "basic-null", json: "null", expectedResult: 0, resultIsNil: true, }, { name: "basic-negative-err", json: "-", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "basic-negative-err", json: "-q", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "basic-null-err", json: "nxll", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "basic-skip-data-err", json: "trua", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "basic-negative2", json: "-123", expectedResult: -123, }, { name: "basic-big", json: " 43", expectedResult: 43, }, { name: "basic-big-overflow", json: " 2147483648", expectedResult: 0, err: true, }, { name: "basic-big-overflow", json: "137", expectedResult: 0, err: true, }, { name: "basic-big-overflow", json: "128", expectedResult: 0, err: true, }, { name: "basic-big-overflow2", json: "21474836483", expectedResult: 0, err: true, }, { name: "basic-float", json: "2.4595", expectedResult: 2, }, { name: "basic-float2", json: "-7.8876", expectedResult: -7, }, { name: "basic-float2", json: "-7.8876a", expectedResult: 0, err: true, }, { name: "basic-exponent-positive-positive-exp", json: "1.2E2", expectedResult: 120, }, { name: "basic-exponent-positive-positive-exp1", json: "3.5e+001 ", expectedResult: 35, }, { name: "basic-exponent-positive-positive-exp1", json: "3.5e+001", expectedResult: 35, }, { name: "basic-exponent-positive-positive-exp2", json: "5e+01", expectedResult: 50, }, { name: "basic-exponent-positive-negative-exp", json: "1e-2 ", expectedResult: 0, }, { name: "basic-exponent-positive-negative-exp2", json: "5E-6", expectedResult: 0, }, { name: "basic-exponent-positive-negative-exp3", json: "3e-3", expectedResult: 0, }, { name: "basic-exponent-positive-negative-exp4", json: "8e-1 ", expectedResult: 0, }, { name: "basic-exponent-positive-negative-exp4", json: "8e1 ", expectedResult: 80, }, { name: "basic-exponent-positive-negative-exp4", json: "8e-1", expectedResult: 0, }, { name: "basic-exponent-negative-positive-exp", json: "-1e2", expectedResult: -100, }, { name: "basic-exponent-negative-positive-exp2", json: "-5e+01", expectedResult: -50, }, { name: "basic-exponent-negative-positive-exp3", json: "-3e01", expectedResult: -30, }, { name: "error3", json: "0E40", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "exponent-err-", json: "0.1e", expectedResult: 0, err: true, }, { name: "exponent-err-too-big", json: "0.1e10000000000000000000", expectedResult: 0, err: true, }, { name: "exponent-err-too-big", json: "0.1932242242424244244e1000000000000000000000000", expectedResult: 0, err: true, }, { name: "basic-exponent-negative-positive-exp4", json: "-8e+001", expectedResult: -80, }, { name: "exponent-err-too-big2", json: "0e100 ", expectedResult: 0, err: true, }, { name: "exponent-err-too-big2", json: "0.1e100 ", expectedResult: 0, err: true, }, { name: "basic-exponent-err", json: "3e", expectedResult: 0, err: true, }, { name: "basic-float", json: "8.32 ", expectedResult: 8, }, { name: "error", json: "83zez4", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "error", json: "8ea00$aa5", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "error2", json: "-8e+00$aa5", expectedResult: 0, err: true, }, { name: "error4", json: "0.E----", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "error5", json: "0E40", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "error6", json: "0.e-9", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "error7", json: "0.e", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "error8", json: "-5.e-2", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "error8", json: "-5.01e", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "invalid-type", json: `"string"`, expectedResult: 0, err: true, errType: InvalidUnmarshalError(""), }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { json := []byte(testCase.json) var v = (*int8)(nil) err := Unmarshal(json, &v) if testCase.err { assert.NotNil(t, err, "Err must not be nil") if testCase.errType != nil { assert.IsType( t, testCase.errType, err, fmt.Sprintf("err should be of type %s", reflect.TypeOf(err).String()), ) } return } assert.Nil(t, err, "Err must be nil") if testCase.resultIsNil { assert.Nil(t, v) } else { assert.Equal(t, testCase.expectedResult, *v, fmt.Sprintf("v must be equal to %d", testCase.expectedResult)) } }) } t.Run("decoder-api-invalid-json", func(t *testing.T) { var v = new(int8) err := Unmarshal([]byte(``), &v) assert.NotNil(t, err, "Err must not be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") }) t.Run("decoder-api-invalid-json2", func(t *testing.T) { var v = new(int8) var dec = NewDecoder(strings.NewReader(``)) err := dec.Int8Null(&v) assert.NotNil(t, err, "Err must not be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") }) } golang-github-francoispqt-gojay-1.2.13/decode_number_test.go000066400000000000000000000012441365331665100241560ustar00rootroot00000000000000package gojay import ( "strings" "testing" "github.com/stretchr/testify/assert" ) func TestDecodeNumberExra(t *testing.T) { t.Run("skip-number-err", func(t *testing.T) { dec := NewDecoder(strings.NewReader("123456afzfz343")) _, err := dec.skipNumber() assert.NotNil(t, err, "err should not be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") }) t.Run("get-exponent-err", func(t *testing.T) { v := 0 dec := NewDecoder(strings.NewReader("1.2Ea")) err := dec.Decode(&v) assert.NotNil(t, err, "err should not be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") }) } golang-github-francoispqt-gojay-1.2.13/decode_number_uint.go000066400000000000000000000441141365331665100241610ustar00rootroot00000000000000package gojay import ( "math" ) // DecodeUint8 reads the next JSON-encoded value from the decoder's input (io.Reader) and stores it in the uint8 pointed to by v. // // See the documentation for Unmarshal for details about the conversion of JSON into a Go value. func (dec *Decoder) DecodeUint8(v *uint8) error { if dec.isPooled == 1 { panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) } return dec.decodeUint8(v) } func (dec *Decoder) decodeUint8(v *uint8) error { for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { switch c := dec.data[dec.cursor]; c { case ' ', '\n', '\t', '\r', ',': continue case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': val, err := dec.getUint8() if err != nil { return err } *v = val return nil case '-': // if negative, we just set it to 0 and set error dec.err = dec.makeInvalidUnmarshalErr(v) err := dec.skipData() if err != nil { return err } return nil case 'n': dec.cursor++ err := dec.assertNull() if err != nil { return err } return nil default: dec.err = dec.makeInvalidUnmarshalErr(v) err := dec.skipData() if err != nil { return err } return nil } } return dec.raiseInvalidJSONErr(dec.cursor) } func (dec *Decoder) decodeUint8Null(v **uint8) error { for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { switch c := dec.data[dec.cursor]; c { case ' ', '\n', '\t', '\r', ',': continue case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': val, err := dec.getUint8() if err != nil { return err } if *v == nil { *v = new(uint8) } **v = val return nil case '-': // if negative, we just set it to 0 and set error dec.err = dec.makeInvalidUnmarshalErr(v) err := dec.skipData() if err != nil { return err } if *v == nil { *v = new(uint8) } return nil case 'n': dec.cursor++ err := dec.assertNull() if err != nil { return err } return nil default: dec.err = dec.makeInvalidUnmarshalErr(v) err := dec.skipData() if err != nil { return err } return nil } } return dec.raiseInvalidJSONErr(dec.cursor) } func (dec *Decoder) getUint8() (uint8, error) { var end = dec.cursor var start = dec.cursor // look for following numbers for j := dec.cursor + 1; j < dec.length || dec.read(); j++ { switch dec.data[j] { case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': end = j continue case ' ', '\n', '\t', '\r': continue case '.', ',', '}', ']': dec.cursor = j return dec.atoui8(start, end), nil } // invalid json we expect numbers, dot (single one), comma, or spaces return 0, dec.raiseInvalidJSONErr(dec.cursor) } return dec.atoui8(start, end), nil } // DecodeUint16 reads the next JSON-encoded value from the decoder's input (io.Reader) and stores it in the uint16 pointed to by v. // // See the documentation for Unmarshal for details about the conversion of JSON into a Go value. func (dec *Decoder) DecodeUint16(v *uint16) error { if dec.isPooled == 1 { panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) } return dec.decodeUint16(v) } func (dec *Decoder) decodeUint16(v *uint16) error { for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { switch c := dec.data[dec.cursor]; c { case ' ', '\n', '\t', '\r', ',': continue case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': val, err := dec.getUint16() if err != nil { return err } *v = val return nil case '-': dec.err = dec.makeInvalidUnmarshalErr(v) err := dec.skipData() if err != nil { return err } return nil case 'n': dec.cursor++ err := dec.assertNull() if err != nil { return err } return nil default: dec.err = dec.makeInvalidUnmarshalErr(v) err := dec.skipData() if err != nil { return err } return nil } } return dec.raiseInvalidJSONErr(dec.cursor) } func (dec *Decoder) decodeUint16Null(v **uint16) error { for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { switch c := dec.data[dec.cursor]; c { case ' ', '\n', '\t', '\r', ',': continue case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': val, err := dec.getUint16() if err != nil { return err } if *v == nil { *v = new(uint16) } **v = val return nil case '-': dec.err = dec.makeInvalidUnmarshalErr(v) err := dec.skipData() if err != nil { return err } if *v == nil { *v = new(uint16) } return nil case 'n': dec.cursor++ err := dec.assertNull() if err != nil { return err } return nil default: dec.err = dec.makeInvalidUnmarshalErr(v) err := dec.skipData() if err != nil { return err } return nil } } return dec.raiseInvalidJSONErr(dec.cursor) } func (dec *Decoder) getUint16() (uint16, error) { var end = dec.cursor var start = dec.cursor // look for following numbers for j := dec.cursor + 1; j < dec.length || dec.read(); j++ { switch dec.data[j] { case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': end = j continue case ' ', '\n', '\t', '\r': continue case '.', ',', '}', ']': dec.cursor = j return dec.atoui16(start, end), nil } // invalid json we expect numbers, dot (single one), comma, or spaces return 0, dec.raiseInvalidJSONErr(dec.cursor) } return dec.atoui16(start, end), nil } // DecodeUint32 reads the next JSON-encoded value from the decoder's input (io.Reader) and stores it in the uint32 pointed to by v. // // See the documentation for Unmarshal for details about the conversion of JSON into a Go value. func (dec *Decoder) DecodeUint32(v *uint32) error { if dec.isPooled == 1 { panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) } return dec.decodeUint32(v) } func (dec *Decoder) decodeUint32(v *uint32) error { for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { switch c := dec.data[dec.cursor]; c { case ' ', '\n', '\t', '\r', ',': continue case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': val, err := dec.getUint32() if err != nil { return err } *v = val return nil case '-': dec.err = dec.makeInvalidUnmarshalErr(v) err := dec.skipData() if err != nil { return err } return nil case 'n': dec.cursor++ err := dec.assertNull() if err != nil { return err } return nil default: dec.err = dec.makeInvalidUnmarshalErr(v) err := dec.skipData() if err != nil { return err } return nil } } return dec.raiseInvalidJSONErr(dec.cursor) } func (dec *Decoder) decodeUint32Null(v **uint32) error { for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { switch c := dec.data[dec.cursor]; c { case ' ', '\n', '\t', '\r', ',': continue case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': val, err := dec.getUint32() if err != nil { return err } if *v == nil { *v = new(uint32) } **v = val return nil case '-': dec.err = dec.makeInvalidUnmarshalErr(v) err := dec.skipData() if err != nil { return err } if *v == nil { *v = new(uint32) } return nil case 'n': dec.cursor++ err := dec.assertNull() if err != nil { return err } return nil default: dec.err = dec.makeInvalidUnmarshalErr(v) err := dec.skipData() if err != nil { return err } return nil } } return dec.raiseInvalidJSONErr(dec.cursor) } func (dec *Decoder) getUint32() (uint32, error) { var end = dec.cursor var start = dec.cursor // look for following numbers for j := dec.cursor + 1; j < dec.length || dec.read(); j++ { switch dec.data[j] { case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': end = j continue case ' ', '\n', '\t', '\r': continue case '.', ',', '}', ']': dec.cursor = j return dec.atoui32(start, end), nil } // invalid json we expect numbers, dot (single one), comma, or spaces return 0, dec.raiseInvalidJSONErr(dec.cursor) } return dec.atoui32(start, end), nil } // DecodeUint64 reads the next JSON-encoded value from the decoder's input (io.Reader) and stores it in the uint64 pointed to by v. // // See the documentation for Unmarshal for details about the conversion of JSON into a Go value. func (dec *Decoder) DecodeUint64(v *uint64) error { if dec.isPooled == 1 { panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) } return dec.decodeUint64(v) } func (dec *Decoder) decodeUint64(v *uint64) error { for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { switch c := dec.data[dec.cursor]; c { case ' ', '\n', '\t', '\r', ',': continue case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': val, err := dec.getUint64() if err != nil { return err } *v = val return nil case '-': dec.err = dec.makeInvalidUnmarshalErr(v) err := dec.skipData() if err != nil { return err } return nil case 'n': dec.cursor++ err := dec.assertNull() if err != nil { return err } return nil default: dec.err = dec.makeInvalidUnmarshalErr(v) err := dec.skipData() if err != nil { return err } return nil } } return dec.raiseInvalidJSONErr(dec.cursor) } func (dec *Decoder) decodeUint64Null(v **uint64) error { for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { switch c := dec.data[dec.cursor]; c { case ' ', '\n', '\t', '\r', ',': continue case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': val, err := dec.getUint64() if err != nil { return err } if *v == nil { *v = new(uint64) } **v = val return nil case '-': dec.err = dec.makeInvalidUnmarshalErr(v) err := dec.skipData() if err != nil { return err } if *v == nil { *v = new(uint64) } return nil case 'n': dec.cursor++ err := dec.assertNull() if err != nil { return err } return nil default: dec.err = dec.makeInvalidUnmarshalErr(v) err := dec.skipData() if err != nil { return err } return nil } } return dec.raiseInvalidJSONErr(dec.cursor) } func (dec *Decoder) getUint64() (uint64, error) { var end = dec.cursor var start = dec.cursor // look for following numbers for j := dec.cursor + 1; j < dec.length || dec.read(); j++ { switch dec.data[j] { case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': end = j continue case ' ', '\n', '\t', '\r', '.', ',', '}', ']': dec.cursor = j return dec.atoui64(start, end), nil } // invalid json we expect numbers, dot (single one), comma, or spaces return 0, dec.raiseInvalidJSONErr(dec.cursor) } return dec.atoui64(start, end), nil } func (dec *Decoder) atoui64(start, end int) uint64 { var ll = end + 1 - start var val = uint64(digits[dec.data[start]]) end = end + 1 if ll < maxUint64Length { for i := start + 1; i < end; i++ { uintv := uint64(digits[dec.data[i]]) val = (val << 3) + (val << 1) + uintv } } else if ll == maxUint64Length { for i := start + 1; i < end; i++ { uintv := uint64(digits[dec.data[i]]) if val > maxUint64toMultiply { dec.err = dec.makeInvalidUnmarshalErr(val) return 0 } val = (val << 3) + (val << 1) if math.MaxUint64-val < uintv { dec.err = dec.makeInvalidUnmarshalErr(val) return 0 } val += uintv } } else { dec.err = dec.makeInvalidUnmarshalErr(val) return 0 } return val } func (dec *Decoder) atoui32(start, end int) uint32 { var ll = end + 1 - start var val uint32 val = uint32(digits[dec.data[start]]) end = end + 1 if ll < maxUint32Length { for i := start + 1; i < end; i++ { uintv := uint32(digits[dec.data[i]]) val = (val << 3) + (val << 1) + uintv } } else if ll == maxUint32Length { for i := start + 1; i < end; i++ { uintv := uint32(digits[dec.data[i]]) if val > maxUint32toMultiply { dec.err = dec.makeInvalidUnmarshalErr(val) return 0 } val = (val << 3) + (val << 1) if math.MaxUint32-val < uintv { dec.err = dec.makeInvalidUnmarshalErr(val) return 0 } val += uintv } } else if ll > maxUint32Length { dec.err = dec.makeInvalidUnmarshalErr(val) val = 0 } return val } func (dec *Decoder) atoui16(start, end int) uint16 { var ll = end + 1 - start var val uint16 val = uint16(digits[dec.data[start]]) end = end + 1 if ll < maxUint16Length { for i := start + 1; i < end; i++ { uintv := uint16(digits[dec.data[i]]) val = (val << 3) + (val << 1) + uintv } } else if ll == maxUint16Length { for i := start + 1; i < end; i++ { uintv := uint16(digits[dec.data[i]]) if val > maxUint16toMultiply { dec.err = dec.makeInvalidUnmarshalErr(val) return 0 } val = (val << 3) + (val << 1) if math.MaxUint16-val < uintv { dec.err = dec.makeInvalidUnmarshalErr(val) return 0 } val += uintv } } else if ll > maxUint16Length { dec.err = dec.makeInvalidUnmarshalErr(val) val = 0 } return val } func (dec *Decoder) atoui8(start, end int) uint8 { var ll = end + 1 - start var val uint8 val = uint8(digits[dec.data[start]]) end = end + 1 if ll < maxUint8Length { for i := start + 1; i < end; i++ { uintv := uint8(digits[dec.data[i]]) val = (val << 3) + (val << 1) + uintv } } else if ll == maxUint8Length { for i := start + 1; i < end; i++ { uintv := uint8(digits[dec.data[i]]) if val > maxUint8toMultiply { dec.err = dec.makeInvalidUnmarshalErr(val) return 0 } val = (val << 3) + (val << 1) if math.MaxUint8-val < uintv { dec.err = dec.makeInvalidUnmarshalErr(val) return 0 } val += uintv } } else if ll > maxUint8Length { dec.err = dec.makeInvalidUnmarshalErr(val) val = 0 } return val } // Add Values functions // AddUint8 decodes the JSON value within an object or an array to an *int. // If next key value overflows uint8, an InvalidUnmarshalError error will be returned. func (dec *Decoder) AddUint8(v *uint8) error { return dec.Uint8(v) } // AddUint8Null decodes the JSON value within an object or an array to an *int. // If next key value overflows uint8, an InvalidUnmarshalError error will be returned. // If a `null` is encountered, gojay does not change the value of the pointer. func (dec *Decoder) AddUint8Null(v **uint8) error { return dec.Uint8Null(v) } // AddUint16 decodes the JSON value within an object or an array to an *int. // If next key value overflows uint16, an InvalidUnmarshalError error will be returned. func (dec *Decoder) AddUint16(v *uint16) error { return dec.Uint16(v) } // AddUint16Null decodes the JSON value within an object or an array to an *int. // If next key value overflows uint16, an InvalidUnmarshalError error will be returned. // If a `null` is encountered, gojay does not change the value of the pointer. func (dec *Decoder) AddUint16Null(v **uint16) error { return dec.Uint16Null(v) } // AddUint32 decodes the JSON value within an object or an array to an *int. // If next key value overflows uint32, an InvalidUnmarshalError error will be returned. func (dec *Decoder) AddUint32(v *uint32) error { return dec.Uint32(v) } // AddUint32Null decodes the JSON value within an object or an array to an *int. // If next key value overflows uint32, an InvalidUnmarshalError error will be returned. // If a `null` is encountered, gojay does not change the value of the pointer. func (dec *Decoder) AddUint32Null(v **uint32) error { return dec.Uint32Null(v) } // AddUint64 decodes the JSON value within an object or an array to an *int. // If next key value overflows uint64, an InvalidUnmarshalError error will be returned. func (dec *Decoder) AddUint64(v *uint64) error { return dec.Uint64(v) } // AddUint64Null decodes the JSON value within an object or an array to an *int. // If next key value overflows uint64, an InvalidUnmarshalError error will be returned. // If a `null` is encountered, gojay does not change the value of the pointer. func (dec *Decoder) AddUint64Null(v **uint64) error { return dec.Uint64Null(v) } // Uint8 decodes the JSON value within an object or an array to an *int. // If next key value overflows uint8, an InvalidUnmarshalError error will be returned. func (dec *Decoder) Uint8(v *uint8) error { err := dec.decodeUint8(v) if err != nil { return err } dec.called |= 1 return nil } // Uint8Null decodes the JSON value within an object or an array to an *int. // If next key value overflows uint8, an InvalidUnmarshalError error will be returned. func (dec *Decoder) Uint8Null(v **uint8) error { err := dec.decodeUint8Null(v) if err != nil { return err } dec.called |= 1 return nil } // Uint16 decodes the JSON value within an object or an array to an *int. // If next key value overflows uint16, an InvalidUnmarshalError error will be returned. func (dec *Decoder) Uint16(v *uint16) error { err := dec.decodeUint16(v) if err != nil { return err } dec.called |= 1 return nil } // Uint16Null decodes the JSON value within an object or an array to an *int. // If next key value overflows uint16, an InvalidUnmarshalError error will be returned. func (dec *Decoder) Uint16Null(v **uint16) error { err := dec.decodeUint16Null(v) if err != nil { return err } dec.called |= 1 return nil } // Uint32 decodes the JSON value within an object or an array to an *int. // If next key value overflows uint32, an InvalidUnmarshalError error will be returned. func (dec *Decoder) Uint32(v *uint32) error { err := dec.decodeUint32(v) if err != nil { return err } dec.called |= 1 return nil } // Uint32Null decodes the JSON value within an object or an array to an *int. // If next key value overflows uint32, an InvalidUnmarshalError error will be returned. func (dec *Decoder) Uint32Null(v **uint32) error { err := dec.decodeUint32Null(v) if err != nil { return err } dec.called |= 1 return nil } // Uint64 decodes the JSON value within an object or an array to an *int. // If next key value overflows uint64, an InvalidUnmarshalError error will be returned. func (dec *Decoder) Uint64(v *uint64) error { err := dec.decodeUint64(v) if err != nil { return err } dec.called |= 1 return nil } // Uint64Null decodes the JSON value within an object or an array to an *int. // If next key value overflows uint64, an InvalidUnmarshalError error will be returned. func (dec *Decoder) Uint64Null(v **uint64) error { err := dec.decodeUint64Null(v) if err != nil { return err } dec.called |= 1 return nil } golang-github-francoispqt-gojay-1.2.13/decode_number_uint_test.go000066400000000000000000000767541365331665100252370ustar00rootroot00000000000000package gojay import ( "fmt" "reflect" "strings" "testing" "github.com/stretchr/testify/assert" ) func TestDecoderUint64(t *testing.T) { testCases := []struct { name string json string expectedResult uint64 err bool errType interface{} }{ { name: "basic-positive", json: "100", expectedResult: 100, }, { name: "basic-positive2", json: " 1039405", expectedResult: 1039405, }, { name: "basic-negative", json: "-2", expectedResult: 0, err: true, }, { name: "basic-null", json: "null", expectedResult: 0, }, { name: "basic-null-err", json: "nxll", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "basic-skip-data-err", json: "trua", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "basic-big", json: "18446744073709551615", expectedResult: 18446744073709551615, }, { name: "basic-big-overflow", json: "18446744073709551616", expectedResult: 0, err: true, }, { name: "basic-big-overflow", json: "18446744073709551625", expectedResult: 0, err: true, }, { name: "basic-big-overflow2", json: "184467440737095516161", expectedResult: 0, err: true, }, { name: "basic-negative2", json: "-2349557", expectedResult: 0, err: true, }, { name: "basic-float", json: "2.4595", expectedResult: 2, }, { name: "basic-float2", json: "-7.8876", expectedResult: 0, err: true, }, { name: "error1", json: "132zz4", expectedResult: 0, err: true, }, { name: "error", json: "-83zez4", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "invalid-type", json: `"string"`, expectedResult: 0, err: true, errType: InvalidUnmarshalError(""), }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { json := []byte(testCase.json) var v uint64 err := Unmarshal(json, &v) if testCase.err { assert.NotNil(t, err, "Err must not be nil") if testCase.errType != nil { assert.IsType( t, testCase.errType, err, fmt.Sprintf("err should be of type %s", reflect.TypeOf(err).String()), ) } } else { assert.Nil(t, err, "Err must be nil") } assert.Equal(t, testCase.expectedResult, v, fmt.Sprintf("v must be equal to %d", testCase.expectedResult)) }) } t.Run("pool-error", func(t *testing.T) { result := uint64(1) dec := NewDecoder(nil) dec.Release() defer func() { err := recover() assert.NotNil(t, err, "err shouldnt be nil") assert.IsType(t, InvalidUsagePooledDecoderError(""), err, "err should be of type InvalidUsagePooledDecoderError") }() _ = dec.DecodeUint64(&result) assert.True(t, false, "should not be called as decoder should have panicked") }) t.Run("decoder-api", func(t *testing.T) { var v uint64 dec := NewDecoder(strings.NewReader(`33`)) defer dec.Release() err := dec.DecodeUint64(&v) assert.Nil(t, err, "Err must be nil") assert.Equal(t, uint64(33), v, "v must be equal to 33") }) t.Run("decoder-api-json-error", func(t *testing.T) { var v uint64 dec := NewDecoder(strings.NewReader(``)) defer dec.Release() err := dec.DecodeUint64(&v) assert.NotNil(t, err, "Err must not be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") }) } func TestDecoderUint64Null(t *testing.T) { testCases := []struct { name string json string expectedResult uint64 err bool errType interface{} resultIsNil bool }{ { name: "basic-positive", json: "100", expectedResult: 100, }, { name: "basic-positive2", json: " 1039405", expectedResult: 1039405, }, { name: "basic-negative", json: "-2", expectedResult: 0, err: true, }, { name: "basic-null", json: "null", expectedResult: 0, resultIsNil: true, }, { name: "basic-null-err", json: "nxll", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "basic-skip-data-err", json: "trua", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "basic-big", json: "18446744073709551615", expectedResult: 18446744073709551615, }, { name: "basic-big-overflow", json: "18446744073709551616", expectedResult: 0, err: true, }, { name: "basic-big-overflow", json: "18446744073709551625", expectedResult: 0, err: true, }, { name: "basic-big-overflow2", json: "184467440737095516161", expectedResult: 0, err: true, }, { name: "basic-negative2", json: "-2349557", expectedResult: 0, err: true, }, { name: "basic-float", json: "2.4595", expectedResult: 2, }, { name: "basic-float2", json: "-7.8876", expectedResult: 0, err: true, }, { name: "error1", json: "132zz4", expectedResult: 0, err: true, }, { name: "error", json: "-83zez4", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "invalid-type", json: `"string"`, expectedResult: 0, err: true, errType: InvalidUnmarshalError(""), }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { json := []byte(testCase.json) var v = (*uint64)(nil) err := Unmarshal(json, &v) if testCase.err { assert.NotNil(t, err, "Err must not be nil") if testCase.errType != nil { assert.IsType( t, testCase.errType, err, fmt.Sprintf("err should be of type %s", reflect.TypeOf(err).String()), ) } return } assert.Nil(t, err, "Err must be nil") if testCase.resultIsNil { assert.Nil(t, v) } else { assert.Equal(t, testCase.expectedResult, *v, fmt.Sprintf("v must be equal to %d", testCase.expectedResult)) } }) } t.Run("decoder-api-invalid-json", func(t *testing.T) { var v = new(uint64) err := Unmarshal([]byte(``), &v) assert.NotNil(t, err, "Err must not be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") }) t.Run("decoder-api-invalid-json2", func(t *testing.T) { var v = new(uint64) var dec = NewDecoder(strings.NewReader(``)) err := dec.Uint64Null(&v) assert.NotNil(t, err, "Err must not be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") }) } func TestDecoderUint32(t *testing.T) { testCases := []struct { name string json string expectedResult uint32 err bool errType interface{} }{ { name: "basic-positive", json: "100", expectedResult: 100, }, { name: "basic-positive2", json: " 1039405 ", expectedResult: 1039405, }, { name: "basic-negative", json: "-2", expectedResult: 0, err: true, }, { name: "basic-null", json: "null", expectedResult: 0, }, { name: "basic-null-err", json: "nxll", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "basic-skip-data-err", json: "trua", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "basic-negative2", json: "-2349557", expectedResult: 0, err: true, }, { name: "basic-big", json: "4294967295", expectedResult: 4294967295, }, { name: "basic-big-overflow", json: " 4294967298", expectedResult: 0, err: true, }, { name: "basic-big-overflow", json: "4294967395", expectedResult: 0, err: true, }, { name: "basic-big-overflow2", json: "42949672983", expectedResult: 0, err: true, }, { name: "basic-float", json: "2.4595", expectedResult: 2, }, { name: "basic-float2", json: "-7.8876", expectedResult: 0, err: true, }, { name: "error", json: "83zez4", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "error", json: "-83zez4", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "invalid-type", json: `"string"`, expectedResult: 0, err: true, errType: InvalidUnmarshalError(""), }, { name: "invalid-json", json: `123invalid`, expectedResult: 0, err: true, errType: InvalidJSONError(""), }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { json := []byte(testCase.json) var v uint32 err := Unmarshal(json, &v) if testCase.err { assert.NotNil(t, err, "Err must not be nil") if testCase.errType != nil { assert.IsType( t, testCase.errType, err, fmt.Sprintf("err should be of type %s", reflect.TypeOf(err).String()), ) } } else { assert.Nil(t, err, "Err must be nil") } assert.Equal(t, testCase.expectedResult, v, fmt.Sprintf("v must be equal to %d", testCase.expectedResult)) }) } t.Run("pool-error", func(t *testing.T) { result := uint32(1) dec := NewDecoder(nil) dec.Release() defer func() { err := recover() assert.NotNil(t, err, "err shouldnt be nil") assert.IsType(t, InvalidUsagePooledDecoderError(""), err, "err should be of type InvalidUsagePooledDecoderError") }() _ = dec.DecodeUint32(&result) assert.True(t, false, "should not be called as decoder should have panicked") }) t.Run("decoder-api", func(t *testing.T) { var v uint32 dec := NewDecoder(strings.NewReader(`33`)) defer dec.Release() err := dec.DecodeUint32(&v) assert.Nil(t, err, "Err must be nil") assert.Equal(t, uint32(33), v, "v must be equal to 33") }) t.Run("decoder-api-json-error", func(t *testing.T) { var v uint32 dec := NewDecoder(strings.NewReader(``)) defer dec.Release() err := dec.DecodeUint32(&v) assert.NotNil(t, err, "Err must not be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") }) } func TestDecoderUint32Null(t *testing.T) { testCases := []struct { name string json string expectedResult uint32 err bool errType interface{} resultIsNil bool }{ { name: "basic-positive", json: "100", expectedResult: 100, }, { name: "basic-positive2", json: " 1039405 ", expectedResult: 1039405, }, { name: "basic-negative", json: "-2", expectedResult: 0, err: true, }, { name: "basic-null", json: "null", expectedResult: 0, resultIsNil: true, }, { name: "basic-null-err", json: "nxll", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "basic-skip-data-err", json: "trua", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "basic-negative2", json: "-2349557", expectedResult: 0, err: true, }, { name: "basic-big", json: "4294967295", expectedResult: 4294967295, }, { name: "basic-big-overflow", json: " 4294967298", expectedResult: 0, err: true, }, { name: "basic-big-overflow", json: "4294967395", expectedResult: 0, err: true, }, { name: "basic-big-overflow2", json: "42949672983", expectedResult: 0, err: true, }, { name: "basic-float", json: "2.4595", expectedResult: 2, }, { name: "basic-float2", json: "-7.8876", expectedResult: 0, err: true, }, { name: "error", json: "83zez4", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "error", json: "-83zez4", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "invalid-type", json: `"string"`, expectedResult: 0, err: true, errType: InvalidUnmarshalError(""), }, { name: "invalid-json", json: `123invalid`, expectedResult: 0, err: true, errType: InvalidJSONError(""), }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { json := []byte(testCase.json) var v = (*uint32)(nil) err := Unmarshal(json, &v) if testCase.err { assert.NotNil(t, err, "Err must not be nil") if testCase.errType != nil { assert.IsType( t, testCase.errType, err, fmt.Sprintf("err should be of type %s", reflect.TypeOf(err).String()), ) } return } assert.Nil(t, err, "Err must be nil") if testCase.resultIsNil { assert.Nil(t, v) } else { assert.Equal(t, testCase.expectedResult, *v, fmt.Sprintf("v must be equal to %d", testCase.expectedResult)) } }) } t.Run("decoder-api-invalid-json", func(t *testing.T) { var v = new(uint32) err := Unmarshal([]byte(``), &v) assert.NotNil(t, err, "Err must not be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") }) t.Run("decoder-api-invalid-json2", func(t *testing.T) { var v = new(uint32) var dec = NewDecoder(strings.NewReader(``)) err := dec.Uint32Null(&v) assert.NotNil(t, err, "Err must not be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") }) } func TestDecoderUint16(t *testing.T) { testCases := []struct { name string json string expectedResult uint16 err bool errType interface{} }{ { name: "basic-positive", json: "100", expectedResult: 100, }, { name: "basic-positive2", json: " 3224 ", expectedResult: 3224, }, { name: "basic-negative", json: "-2", expectedResult: 0, err: true, }, { name: "basic-null", json: "null", expectedResult: 0, }, { name: "basic-null-err", json: "nxll", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "basic-skip-data-err", json: "trua", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "basic-skip-data-err", json: "trua", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "basic-overflow", json: "335346564", expectedResult: 0, err: true, errType: InvalidUnmarshalError(""), }, { name: "basic-negative2", json: "-24467", expectedResult: 0, err: true, }, { name: "basic-big", json: "54546", expectedResult: 54546, }, { name: "basic-big-overflow", json: " 4294967298", expectedResult: 0, err: true, }, { name: "basic-big-overflow", json: " 65537", expectedResult: 0, err: true, }, { name: "basic-big-overflow", json: " 66537", expectedResult: 0, err: true, }, { name: "basic-big-overflow2", json: "42949672983", expectedResult: 0, err: true, }, { name: "basic-float", json: "2.4595", expectedResult: 2, }, { name: "basic-float2", json: "-7.8876", expectedResult: 0, err: true, }, { name: "error", json: "83zez4", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "error", json: "-83zez4", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "invalid-type", json: `"string"`, expectedResult: 0, err: true, errType: InvalidUnmarshalError(""), }, { name: "invalid-json", json: `123invalid`, expectedResult: 0, err: true, errType: InvalidJSONError(""), }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { json := []byte(testCase.json) var v uint16 err := Unmarshal(json, &v) if testCase.err { assert.NotNil(t, err, "Err must not be nil") if testCase.errType != nil { assert.IsType( t, testCase.errType, err, fmt.Sprintf("err should be of type %s", reflect.TypeOf(err).String()), ) } } else { assert.Nil(t, err, "Err must be nil") } assert.Equal(t, testCase.expectedResult, v, fmt.Sprintf("v must be equal to %d", testCase.expectedResult)) }) } t.Run("pool-error", func(t *testing.T) { result := uint16(1) dec := NewDecoder(nil) dec.Release() defer func() { err := recover() assert.NotNil(t, err, "err shouldnt be nil") assert.IsType(t, InvalidUsagePooledDecoderError(""), err, "err should be of type InvalidUsagePooledDecoderError") }() _ = dec.DecodeUint16(&result) assert.True(t, false, "should not be called as decoder should have panicked") }) t.Run("decoder-api", func(t *testing.T) { var v uint16 dec := NewDecoder(strings.NewReader(`33`)) defer dec.Release() err := dec.DecodeUint16(&v) assert.Nil(t, err, "Err must be nil") assert.Equal(t, uint16(33), v, "v must be equal to 33") }) t.Run("decoder-api2", func(t *testing.T) { var v uint16 dec := NewDecoder(strings.NewReader(`33`)) defer dec.Release() err := dec.Decode(&v) assert.Nil(t, err, "Err must be nil") assert.Equal(t, uint16(33), v, "v must be equal to 33") }) t.Run("decoder-api-json-error", func(t *testing.T) { var v uint16 dec := NewDecoder(strings.NewReader(``)) defer dec.Release() err := dec.DecodeUint16(&v) assert.NotNil(t, err, "Err must not be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") }) } func TestDecoderUint16Null(t *testing.T) { testCases := []struct { name string json string expectedResult uint16 err bool errType interface{} resultIsNil bool }{ { name: "basic-positive", json: "100", expectedResult: 100, }, { name: "basic-positive2", json: " 3224 ", expectedResult: 3224, }, { name: "basic-negative", json: "-2", expectedResult: 0, err: true, }, { name: "basic-null", json: "null", expectedResult: 0, resultIsNil: true, }, { name: "basic-null-err", json: "nxll", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "basic-skip-data-err", json: "trua", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "basic-skip-data-err", json: "trua", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "basic-overflow", json: "335346564", expectedResult: 0, err: true, errType: InvalidUnmarshalError(""), }, { name: "basic-negative2", json: "-24467", expectedResult: 0, err: true, }, { name: "basic-big", json: "54546", expectedResult: 54546, }, { name: "basic-big-overflow", json: " 4294967298", expectedResult: 0, err: true, }, { name: "basic-big-overflow", json: " 65537", expectedResult: 0, err: true, }, { name: "basic-big-overflow", json: " 66537", expectedResult: 0, err: true, }, { name: "basic-big-overflow2", json: "42949672983", expectedResult: 0, err: true, }, { name: "basic-float", json: "2.4595", expectedResult: 2, }, { name: "basic-float2", json: "-7.8876", expectedResult: 0, err: true, }, { name: "error", json: "83zez4", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "error", json: "-83zez4", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "invalid-type", json: `"string"`, expectedResult: 0, err: true, errType: InvalidUnmarshalError(""), }, { name: "invalid-json", json: `123invalid`, expectedResult: 0, err: true, errType: InvalidJSONError(""), }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { json := []byte(testCase.json) var v = (*uint16)(nil) err := Unmarshal(json, &v) if testCase.err { assert.NotNil(t, err, "Err must not be nil") if testCase.errType != nil { assert.IsType( t, testCase.errType, err, fmt.Sprintf("err should be of type %s", reflect.TypeOf(err).String()), ) } return } assert.Nil(t, err, "Err must be nil") if testCase.resultIsNil { assert.Nil(t, v) } else { assert.Equal(t, testCase.expectedResult, *v, fmt.Sprintf("v must be equal to %d", testCase.expectedResult)) } }) } t.Run("decoder-api-invalid-json", func(t *testing.T) { var v = new(uint16) err := Unmarshal([]byte(``), &v) assert.NotNil(t, err, "Err must not be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") }) t.Run("decoder-api-invalid-json2", func(t *testing.T) { var v = new(uint16) var dec = NewDecoder(strings.NewReader(``)) err := dec.Uint16Null(&v) assert.NotNil(t, err, "Err must not be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") }) } func TestDecoderUint8(t *testing.T) { testCases := []struct { name string json string expectedResult uint8 err bool errType interface{} }{ { name: "basic-positive", json: "100", expectedResult: 100, }, { name: "basic-positive2", json: " 255 ", expectedResult: 255, }, { name: "basic-negative", json: "-2", expectedResult: 0, err: true, }, { name: "basic-null", json: "null", expectedResult: 0, }, { name: "basic-null-err", json: "nxll", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "basic-skip-data-err", json: "trua", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "basic-negative2", json: "-234", expectedResult: 0, err: true, }, { name: "basic-big", json: "200", expectedResult: 200, }, { name: "basic-overflow", json: "256", expectedResult: 0, err: true, errType: InvalidUnmarshalError(""), }, { name: "basic-overflow", json: "274", expectedResult: 0, err: true, errType: InvalidUnmarshalError(""), }, { name: "basic-big-overflow", json: " 4294967298", expectedResult: 0, err: true, }, { name: "basic-big-overflow2", json: "42949672983", expectedResult: 0, err: true, }, { name: "basic-float", json: "2.4595", expectedResult: 2, }, { name: "basic-float2", json: "-7.8876", expectedResult: 0, err: true, }, { name: "error", json: "83zez4", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "error", json: "-83zez4", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "invalid-type", json: `"string"`, expectedResult: 0, err: true, errType: InvalidUnmarshalError(""), }, { name: "invalid-json", json: `123invalid`, expectedResult: 0, err: true, errType: InvalidJSONError(""), }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { json := []byte(testCase.json) var v uint8 err := Unmarshal(json, &v) if testCase.err { assert.NotNil(t, err, "Err must not be nil") if testCase.errType != nil { assert.IsType( t, testCase.errType, err, fmt.Sprintf("err should be of type %s", reflect.TypeOf(err).String()), ) } } else { assert.Nil(t, err, "Err must be nil") } assert.Equal(t, testCase.expectedResult, v, fmt.Sprintf("v must be equal to %d", testCase.expectedResult)) }) } t.Run("pool-error", func(t *testing.T) { result := uint8(1) dec := NewDecoder(nil) dec.Release() defer func() { err := recover() assert.NotNil(t, err, "err shouldnt be nil") assert.IsType(t, InvalidUsagePooledDecoderError(""), err, "err should be of type InvalidUsagePooledDecoderError") }() _ = dec.DecodeUint8(&result) assert.True(t, false, "should not be called as decoder should have panicked") }) t.Run("decoder-api", func(t *testing.T) { var v uint8 dec := NewDecoder(strings.NewReader(`33`)) defer dec.Release() err := dec.DecodeUint8(&v) assert.Nil(t, err, "Err must be nil") assert.Equal(t, uint8(33), v, "v must be equal to 33") }) t.Run("decoder-api2", func(t *testing.T) { var v uint8 dec := NewDecoder(strings.NewReader(`33`)) defer dec.Release() err := dec.Decode(&v) assert.Nil(t, err, "Err must be nil") assert.Equal(t, uint8(33), v, "v must be equal to 33") }) t.Run("decoder-api-json-error", func(t *testing.T) { var v uint8 dec := NewDecoder(strings.NewReader(``)) defer dec.Release() err := dec.DecodeUint8(&v) assert.NotNil(t, err, "Err must not be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") }) } func TestDecoderUint8Null(t *testing.T) { testCases := []struct { name string json string expectedResult uint8 err bool errType interface{} resultIsNil bool }{ { name: "basic-positive", json: "100", expectedResult: 100, }, { name: "basic-positive2", json: " 255 ", expectedResult: 255, }, { name: "basic-negative", json: "-2", expectedResult: 0, err: true, }, { name: "basic-null", json: "null", expectedResult: 0, resultIsNil: true, }, { name: "basic-null-err", json: "nxll", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "basic-skip-data-err", json: "trua", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "basic-negative2", json: "-234", expectedResult: 0, err: true, }, { name: "basic-big", json: "200", expectedResult: 200, }, { name: "basic-overflow", json: "256", expectedResult: 0, err: true, errType: InvalidUnmarshalError(""), }, { name: "basic-overflow", json: "274", expectedResult: 0, err: true, errType: InvalidUnmarshalError(""), }, { name: "basic-big-overflow", json: " 4294967298", expectedResult: 0, err: true, }, { name: "basic-big-overflow2", json: "42949672983", expectedResult: 0, err: true, }, { name: "basic-float", json: "2.4595", expectedResult: 2, }, { name: "basic-float2", json: "-7.8876", expectedResult: 0, err: true, }, { name: "error", json: "83zez4", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "error", json: "-83zez4", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "invalid-type", json: `"string"`, expectedResult: 0, err: true, errType: InvalidUnmarshalError(""), }, { name: "invalid-json", json: `123invalid`, expectedResult: 0, err: true, errType: InvalidJSONError(""), }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { json := []byte(testCase.json) var v = (*uint8)(nil) err := Unmarshal(json, &v) if testCase.err { assert.NotNil(t, err, "Err must not be nil") if testCase.errType != nil { assert.IsType( t, testCase.errType, err, fmt.Sprintf("err should be of type %s", reflect.TypeOf(err).String()), ) } return } assert.Nil(t, err, "Err must be nil") if testCase.resultIsNil { assert.Nil(t, v) } else { assert.Equal(t, testCase.expectedResult, *v, fmt.Sprintf("v must be equal to %d", testCase.expectedResult)) } }) } t.Run("decoder-api-invalid-json", func(t *testing.T) { var v = new(uint8) err := Unmarshal([]byte(``), &v) assert.NotNil(t, err, "Err must not be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") }) t.Run("decoder-api-invalid-json2", func(t *testing.T) { var v = new(uint8) var dec = NewDecoder(strings.NewReader(``)) err := dec.Uint8Null(&v) assert.NotNil(t, err, "Err must not be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") }) } golang-github-francoispqt-gojay-1.2.13/decode_object.go000066400000000000000000000236761365331665100231120ustar00rootroot00000000000000package gojay import ( "reflect" "unsafe" ) // DecodeObject reads the next JSON-encoded value from the decoder's input (io.Reader) and stores it in the value pointed to by v. // // v must implement UnmarshalerJSONObject. // // See the documentation for Unmarshal for details about the conversion of JSON into a Go value. func (dec *Decoder) DecodeObject(j UnmarshalerJSONObject) error { if dec.isPooled == 1 { panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) } _, err := dec.decodeObject(j) return err } func (dec *Decoder) decodeObject(j UnmarshalerJSONObject) (int, error) { keys := j.NKeys() for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { switch dec.data[dec.cursor] { case ' ', '\n', '\t', '\r', ',': case '{': dec.cursor = dec.cursor + 1 // if keys is zero we will parse all keys // we run two loops for micro optimization if keys == 0 { for dec.cursor < dec.length || dec.read() { k, done, err := dec.nextKey() if err != nil { return 0, err } else if done { return dec.cursor, nil } err = j.UnmarshalJSONObject(dec, k) if err != nil { dec.err = err return 0, err } else if dec.called&1 == 0 { err := dec.skipData() if err != nil { return 0, err } } else { dec.keysDone++ } dec.called &= 0 } } else { for (dec.cursor < dec.length || dec.read()) && dec.keysDone < keys { k, done, err := dec.nextKey() if err != nil { return 0, err } else if done { return dec.cursor, nil } err = j.UnmarshalJSONObject(dec, k) if err != nil { dec.err = err return 0, err } else if dec.called&1 == 0 { err := dec.skipData() if err != nil { return 0, err } } else { dec.keysDone++ } dec.called &= 0 } } // will get to that point when keysDone is not lower than keys anymore // in that case, we make sure cursor goes to the end of object, but we skip // unmarshalling if dec.child&1 != 0 { end, err := dec.skipObject() dec.cursor = end return dec.cursor, err } return dec.cursor, nil case 'n': dec.cursor++ err := dec.assertNull() if err != nil { return 0, err } return dec.cursor, nil default: // can't unmarshal to struct dec.err = dec.makeInvalidUnmarshalErr(j) err := dec.skipData() if err != nil { return 0, err } return dec.cursor, nil } } return 0, dec.raiseInvalidJSONErr(dec.cursor) } func (dec *Decoder) decodeObjectNull(v interface{}) (int, error) { // make sure the value is a pointer vv := reflect.ValueOf(v) vvt := vv.Type() if vvt.Kind() != reflect.Ptr || vvt.Elem().Kind() != reflect.Ptr { dec.err = ErrUnmarshalPtrExpected return 0, dec.err } for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { switch dec.data[dec.cursor] { case ' ', '\n', '\t', '\r', ',': case '{': elt := vv.Elem() n := reflect.New(elt.Type().Elem()) elt.Set(n) var j UnmarshalerJSONObject var ok bool if j, ok = n.Interface().(UnmarshalerJSONObject); !ok { dec.err = dec.makeInvalidUnmarshalErr((UnmarshalerJSONObject)(nil)) return 0, dec.err } keys := j.NKeys() dec.cursor = dec.cursor + 1 // if keys is zero we will parse all keys // we run two loops for micro optimization if keys == 0 { for dec.cursor < dec.length || dec.read() { k, done, err := dec.nextKey() if err != nil { return 0, err } else if done { return dec.cursor, nil } err = j.UnmarshalJSONObject(dec, k) if err != nil { dec.err = err return 0, err } else if dec.called&1 == 0 { err := dec.skipData() if err != nil { return 0, err } } else { dec.keysDone++ } dec.called &= 0 } } else { for (dec.cursor < dec.length || dec.read()) && dec.keysDone < keys { k, done, err := dec.nextKey() if err != nil { return 0, err } else if done { return dec.cursor, nil } err = j.UnmarshalJSONObject(dec, k) if err != nil { dec.err = err return 0, err } else if dec.called&1 == 0 { err := dec.skipData() if err != nil { return 0, err } } else { dec.keysDone++ } dec.called &= 0 } } // will get to that point when keysDone is not lower than keys anymore // in that case, we make sure cursor goes to the end of object, but we skip // unmarshalling if dec.child&1 != 0 { end, err := dec.skipObject() dec.cursor = end return dec.cursor, err } return dec.cursor, nil case 'n': dec.cursor++ err := dec.assertNull() if err != nil { return 0, err } return dec.cursor, nil default: // can't unmarshal to struct dec.err = dec.makeInvalidUnmarshalErr((UnmarshalerJSONObject)(nil)) err := dec.skipData() if err != nil { return 0, err } return dec.cursor, nil } } return 0, dec.raiseInvalidJSONErr(dec.cursor) } func (dec *Decoder) skipObject() (int, error) { var objectsOpen = 1 var objectsClosed = 0 for j := dec.cursor; j < dec.length || dec.read(); j++ { switch dec.data[j] { case '}': objectsClosed++ // everything is closed return if objectsOpen == objectsClosed { // add char to object data return j + 1, nil } case '{': objectsOpen++ case '"': j++ var isInEscapeSeq bool var isFirstQuote = true for ; j < dec.length || dec.read(); j++ { if dec.data[j] != '"' { continue } if dec.data[j-1] != '\\' || (!isInEscapeSeq && !isFirstQuote) { break } else { isInEscapeSeq = false } if isFirstQuote { isFirstQuote = false } // loop backward and count how many anti slash found // to see if string is effectively escaped ct := 0 for i := j - 1; i > 0; i-- { if dec.data[i] != '\\' { break } ct++ } // is pair number of slashes, quote is not escaped if ct&1 == 0 { break } isInEscapeSeq = true } default: continue } } return 0, dec.raiseInvalidJSONErr(dec.cursor) } func (dec *Decoder) nextKey() (string, bool, error) { for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { switch dec.data[dec.cursor] { case ' ', '\n', '\t', '\r', ',': continue case '"': dec.cursor = dec.cursor + 1 start, end, err := dec.getString() if err != nil { return "", false, err } var found byte for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { if dec.data[dec.cursor] == ':' { found |= 1 break } } if found&1 != 0 { dec.cursor++ d := dec.data[start : end-1] return *(*string)(unsafe.Pointer(&d)), false, nil } return "", false, dec.raiseInvalidJSONErr(dec.cursor) case '}': dec.cursor = dec.cursor + 1 return "", true, nil default: // can't unmarshall to struct return "", false, dec.raiseInvalidJSONErr(dec.cursor) } } return "", false, dec.raiseInvalidJSONErr(dec.cursor) } func (dec *Decoder) skipData() error { for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { switch dec.data[dec.cursor] { case ' ', '\n', '\t', '\r', ',': continue // is null case 'n': dec.cursor++ err := dec.assertNull() if err != nil { return err } return nil case 't': dec.cursor++ err := dec.assertTrue() if err != nil { return err } return nil // is false case 'f': dec.cursor++ err := dec.assertFalse() if err != nil { return err } return nil // is an object case '{': dec.cursor = dec.cursor + 1 end, err := dec.skipObject() dec.cursor = end return err // is string case '"': dec.cursor = dec.cursor + 1 err := dec.skipString() return err // is array case '[': dec.cursor = dec.cursor + 1 end, err := dec.skipArray() dec.cursor = end return err case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-': end, err := dec.skipNumber() dec.cursor = end return err } return dec.raiseInvalidJSONErr(dec.cursor) } return dec.raiseInvalidJSONErr(dec.cursor) } // DecodeObjectFunc is a func type implementing UnmarshalerJSONObject. // Use it to cast a `func(*Decoder, k string) error` to Unmarshal an object on the fly. type DecodeObjectFunc func(*Decoder, string) error // UnmarshalJSONObject implements UnmarshalerJSONObject. func (f DecodeObjectFunc) UnmarshalJSONObject(dec *Decoder, k string) error { return f(dec, k) } // NKeys implements UnmarshalerJSONObject. func (f DecodeObjectFunc) NKeys() int { return 0 } // Add Values functions // AddObject decodes the JSON value within an object or an array to a UnmarshalerJSONObject. func (dec *Decoder) AddObject(v UnmarshalerJSONObject) error { return dec.Object(v) } // AddObjectNull decodes the JSON value within an object or an array to a UnmarshalerJSONObject. func (dec *Decoder) AddObjectNull(v interface{}) error { return dec.ObjectNull(v) } // Object decodes the JSON value within an object or an array to a UnmarshalerJSONObject. func (dec *Decoder) Object(value UnmarshalerJSONObject) error { initialKeysDone := dec.keysDone initialChild := dec.child dec.keysDone = 0 dec.called = 0 dec.child |= 1 newCursor, err := dec.decodeObject(value) if err != nil { return err } dec.cursor = newCursor dec.keysDone = initialKeysDone dec.child = initialChild dec.called |= 1 return nil } // ObjectNull decodes the JSON value within an object or an array to a UnmarshalerJSONObject. // v should be a pointer to an UnmarshalerJSONObject, // if `null` value is encountered in JSON, it will leave the value v untouched, // else it will create a new instance of the UnmarshalerJSONObject behind v. func (dec *Decoder) ObjectNull(v interface{}) error { initialKeysDone := dec.keysDone initialChild := dec.child dec.keysDone = 0 dec.called = 0 dec.child |= 1 newCursor, err := dec.decodeObjectNull(v) if err != nil { return err } dec.cursor = newCursor dec.keysDone = initialKeysDone dec.child = initialChild dec.called |= 1 return nil } golang-github-francoispqt-gojay-1.2.13/decode_object_test.go000066400000000000000000001357221365331665100241450ustar00rootroot00000000000000package gojay import ( "fmt" "io" "reflect" "strings" "testing" "github.com/stretchr/testify/assert" ) func makePointer(v interface{}) interface{} { var ptr = reflect.New(reflect.TypeOf(v)) ptr.Elem().Set(reflect.ValueOf(v)) return ptr.Interface() } func TestDecodeObjectBasic(t *testing.T) { testCases := []struct { name string json string expectedResult testObject err bool errType interface{} skipCheckResult bool }{ { name: "basic", json: `{ "testStr": "hello world!", "testStrNull": "hello world!", "testInt": 4535, "testIntNull": 4535, "testBool": true, "testBoolNull": true, "testFloat32": 2.345, "testFloat32Null": 2.345, "testFloat64": 123.677, "testFloat64Null": 123.677, "testInt8": 23, "testInt8Null": 23, "testInt16": 1245, "testInt16Null": 1245, "testInt32": 456778, "testInt32Null": 456778, "testInt64": 1446685358, "testInt64Null": 1446685358, "testUint8": 255, "testUint8Null": 255, "testUint16": 3455, "testUint16Null": 3455, "testUint32": 343443, "testUint32Null": 343443, "testUint64": 545665757, "testUint64Null": 545665757, "testSubObjectNull": { "testStr": "1" } }`, expectedResult: testObject{ testStr: "hello world!", testStrNull: makePointer("hello world!").(*string), testInt: 4535, testIntNull: makePointer(4535).(*int), testBool: true, testBoolNull: makePointer(true).(*bool), testFloat32: 2.345, testFloat32Null: makePointer(float32(2.345)).(*float32), testFloat64: 123.677, testFloat64Null: makePointer(float64(123.677)).(*float64), testInt8: 23, testInt8Null: makePointer(int8(23)).(*int8), testInt16: 1245, testInt16Null: makePointer(int16(1245)).(*int16), testInt32: 456778, testInt32Null: makePointer(int32(456778)).(*int32), testInt64: 1446685358, testInt64Null: makePointer(int64(1446685358)).(*int64), testUint8: 255, testUint8Null: makePointer(uint8(255)).(*uint8), testUint16: 3455, testUint16Null: makePointer(uint16(3455)).(*uint16), testUint32: 343443, testUint32Null: makePointer(uint32(343443)).(*uint32), testUint64: 545665757, testUint64Null: makePointer(uint64(545665757)).(*uint64), }, err: false, }, { name: "basic-with-exponent", json: `{ "testStr": "hello world!", "testInt": 3e3, "testBool": true, "testFloat32": 2.345, "testFloat64": 123.677, "testInt8": 23, "testInt16": 1245, "testInt32": 456778, "testInt64": 1446685358, "testUint8": 255, "testUint16": 3455, "testUint32": 343443, "testUint64": 545665757 }`, expectedResult: testObject{ testStr: "hello world!", testInt: 3000, testBool: true, testFloat32: 2.345, testFloat64: 123.677, testInt8: 23, testInt16: 1245, testInt32: 456778, testInt64: 1446685358, testUint8: 255, testUint16: 3455, testUint32: 343443, testUint64: 545665757, }, err: false, }, { name: "basic-with-exponent3", json: `{ "testStr": "hello world!", "testInt": 3e-3, "testBool": true, "testFloat32": 2.345, "testFloat64": 12e-3, "testInt8": 23, "testInt16": 1245, "testInt32": 456778, "testInt64": 1446685358, "testUint8": 255, "testUint16": 3455, "testUint32": 343443, "testUint64": 545665757 }`, expectedResult: testObject{ testStr: "hello world!", testInt: 0, testBool: true, testFloat32: 2.345, testFloat64: 0.012, testInt8: 23, testInt16: 1245, testInt32: 456778, testInt64: 1446685358, testUint8: 255, testUint16: 3455, testUint32: 343443, testUint64: 545665757, }, err: false, }, { name: "basic-err-invalid-type", json: `1`, expectedResult: testObject{}, err: true, errType: InvalidUnmarshalError(""), }, { name: "basic-err-invalid-json", json: `hello`, expectedResult: testObject{}, err: true, errType: InvalidJSONError(""), }, { name: "basic-err-invalid-json", json: `nall`, expectedResult: testObject{}, err: true, errType: InvalidJSONError(""), }, { name: "basic-err-invalid-type", json: ``, expectedResult: testObject{}, err: true, errType: InvalidJSONError(""), }, { name: "basic-err", json: `{ "testStr": "hello world!", "testInt": 453q5, "testBool": trae, "testFloat32": 2q.345, "testFloat64": 12x3.677, "testInt8": 2s3, "testInt16": 1245, "testInt32": 4567q78, "testInt64": 14466e85358, "testUint8": 2s55, "testUint16": 345i5, "testUint32": 343q443, "testUint64": 5456657z57 }`, expectedResult: testObject{}, err: true, }, { name: "basic-err2", json: `{ "testStr": "hello world!", "testInt": 4535, "testBool": true, "testFloat32": 2.345, "testFloat64": 123.677, "testInt8": 23, "testInt16": 1245, "testInt32": 4567x78, "testInt64": 1446685358, "testUint8": 255, "testUint16": 3455, "testUint32": 343443, "testUint64": 545665757 }`, expectedResult: testObject{}, err: true, }, { name: "basic-err-float32", json: `{ "testStr": "hello world!", "testInt": 4535, "testBool": true, "testFloat32": 2q.345, "testFloat64": 123.677, "testInt8": 23, "testInt16": 1245, "testInt32": 456778, "testInt64": 1446685358, "testUint8": 255, "testUint16": 3455, "testUint32": 343443, "testUint64": 545665757 }`, expectedResult: testObject{}, err: true, }, { name: "basic-err-float64", json: `{ "testStr": "hello world!", "testInt": 4535, "testBool": true, "testFloat32": 2.345, "testFloat64": 1x23.677, "testInt8": 23, "testInt16": 1245, "testInt32": 456778, "testInt64": 1446685358, "testUint8": 255, "testUint16": 3455, "testUint32": 343443, "testUint64": 545665757 }`, expectedResult: testObject{}, err: true, }, { name: "basic-err3", json: `{ "testStr": "hello world!", "testInt": 4535, "testBool": true, "testFloat32": 2.345, "testFloat64": 123.677, "testInt8": 2q3, "testInt16": 1245, "testInt32": 456778, "testInt64": 1446685358, "testUint8": 255, "testUint16": 3455, "testUint32": 343443, "testUint64": 545665757 }`, expectedResult: testObject{}, err: true, }, { name: "basic-err-int16", json: `{ "testStr": "hello world!", "testInt": 4535, "testBool": true, "testFloat32": 2.345, "testFloat64": 123.677, "testInt8": 23, "testInt16": 1x245, "testInt32": 456778, "testInt64": 1446685358, "testUint8": 255, "testUint16": 3455, "testUint32": 343443, "testUint64": 545665757 }`, expectedResult: testObject{}, err: true, }, { name: "basic-err-int64", json: `{ "testStr": "hello world!", "testInt": 4535, "testBool": true, "testFloat32": 2.345, "testFloat64": 123.677, "testInt8": 23, "testInt16": 1245, "testInt32": 456778, "testInt64": 1446q685358, "testUint8": 255, "testUint16": 3455, "testUint32": 343443, "testUint64": 545665757 }`, expectedResult: testObject{}, err: true, }, { name: "basic-err-uint8", json: `{ "testStr": "hello world!", "testInt": 4535, "testBool": true, "testFloat32": 2.345, "testFloat64": 123.677, "testInt8": 23, "testInt16": 1245, "testInt32": 456778, "testInt64": 1446685358, "testUint8": 2x55, "testUint16": 3455, "testUint32": 343443, "testUint64": 545665757 }`, expectedResult: testObject{}, err: true, }, { name: "basic-err-uint16", json: `{ "testStr": "hello world!", "testInt": 4535, "testBool": true, "testFloat32": 2.345, "testFloat64": 123.677, "testInt8": 23, "testInt16": 1245, "testInt32": 456778, "testInt64": 1446685358, "testUint8": 255, "testUint16": 3x455, "testUint32": 343443, "testUint64": 545665757 }`, expectedResult: testObject{}, err: true, }, { name: "basic-err-uint32", json: `{ "testStr": "hello world!", "testInt": 4535, "testBool": true, "testFloat32": 2.345, "testFloat64": 123.677, "testInt8": 23, "testInt16": 1245, "testInt32": 456778, "testInt64": 1446685358, "testUint8": 255, "testUint16": 3455, "testUint32": 3x43443, "testUint64": 545665757 }`, expectedResult: testObject{}, err: true, }, { name: "basic-err-uint64", json: `{ "testStr": "hello world!", "testInt": 4535, "testBool": true, "testFloat32": 2.345, "testFloat64": 123.677, "testInt8": 23, "testInt16": 1245, "testInt32": 456778, "testInt64": 1446685358, "testUint8": 255, "testUint16": 3455, "testUint32": 343443, "testUint64": 5456x65757 }`, expectedResult: testObject{}, err: true, }, { name: "basic-skip-data", json: `{ "testStr": "hello world!", "testInt": 4535, "testBool": true, "testFloat32": 2.345, "testFloat64": 123.677, "testInt8": 23, "skipObject": { "escapedString": "string with escaped \\n new line" }, "testInt16": 1245, "testInt32": 456778, "testInt64": 1446685358, "testUint8": 255, "skipArray": [[],[],{}], "testUint16": 3455, "skipBool": true, "skipNull": null, "testUint32": 343443, "testUint64": 545665757, "skipString": "skipping string with escaped \\n new line", "skipInt": 3, }`, expectedResult: testObject{ testStr: "hello world!", testInt: 4535, testBool: true, testFloat32: 2.345, testFloat64: 123.677, testInt8: 23, testInt16: 1245, testInt32: 456778, testInt64: 1446685358, testUint8: 255, testUint16: 3455, testUint32: 343443, testUint64: 545665757, }, err: false, }, { name: "basic-skip-data-error-uint8-negative", json: `{ "testStr": "hello world!", "testInt": 4535, "testBool": true, "testFloat32": 2.345, "testFloat64": 123.677, "testInt8": 23, "skipObject": { "escapedString": "string with escaped \\n new line" }, "testInt16": 1245, "testInt32": 456778, "testInt64": 1446685358, "testUint8": -255, "skipArray": [[],[],{}], "testUint16": 3455, "skipBool": true, "skipNull": null, "testUint32": 343443, "testUint64": 545665757, "skipString": "skipping string with escaped \\n new line", "skipInt": 3 }`, expectedResult: testObject{ testStr: "hello world!", testInt: 4535, testBool: true, testFloat32: 2.345, testFloat64: 123.677, testInt8: 23, testInt16: 1245, testInt32: 456778, testInt64: 1446685358, testUint8: 0, testUint16: 3455, testUint32: 343443, testUint64: 545665757, }, err: true, }, { name: "skip-data-with-unicode", json: `{ "skipString": "hello\u1234\u2123", "testStr": "hello world!", "testInt": 4535, "testBool": true, "testFloat32": 2.345, "testFloat64": 123.677, "testInt8": 23, "skipObject": { "escapedString": "string with unicode \u1234\u1234\u1234" }, "testInt16": 1245, "testInt32": 456778, "testInt64": 1446685358, "testUint8": 255, "skipArray": [[],[],{}], "testUint16": 3455, "skipBool": true, "skipNull": null, "testUint32": 343443, "testUint64": 545665757, "skipInt": 3 }`, expectedResult: testObject{ testStr: "hello world!", testInt: 4535, testBool: true, testFloat32: 2.345, testFloat64: 123.677, testInt8: 23, testInt16: 1245, testInt32: 456778, testInt64: 1446685358, testUint8: 255, testUint16: 3455, testUint32: 343443, testUint64: 545665757, }, err: false, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { s := testObject{} dec := BorrowDecoder(strings.NewReader(testCase.json)) defer dec.Release() err := dec.Decode(&s) if testCase.err { t.Log(err) assert.NotNil(t, err, "err should not be nil") if testCase.errType != nil { assert.IsType(t, testCase.errType, err, "err should be of the given type") } return } assert.Nil(t, err, "err should be nil") if !testCase.skipCheckResult { assert.Equal(t, testCase.expectedResult, s, "value at given index should be the same as expected results") } }) } } func TestDecodeObjectBasic0Keys(t *testing.T) { testCases := []struct { name string json string expectedResult testObject0Keys err bool errType interface{} skipCheckResult bool }{ { name: "basic", json: `{ "testStr": "hello world!", "testInt": 4535, "testBool": true, "testFloat32": 2.345, "testFloat64": 123.677, "testInt8": 23, "testInt16": 1245, "testInt32": 456778, "testInt64": 1446685358, "testUint8": 255, "testUint16": 3455, "testUint32": 343443, "testUint64": 545665757 }`, expectedResult: testObject0Keys{ testStr: "hello world!", testInt: 4535, testBool: true, testFloat32: 2.345, testFloat64: 123.677, testInt8: 23, testInt16: 1245, testInt32: 456778, testInt64: 1446685358, testUint8: 255, testUint16: 3455, testUint32: 343443, testUint64: 545665757, }, err: false, }, { name: "basic-err-invalid-type", json: `1`, expectedResult: testObject0Keys{}, err: true, errType: InvalidUnmarshalError(""), }, { name: "basic-err-invalid-json", json: `hello`, expectedResult: testObject0Keys{}, err: true, errType: InvalidJSONError(""), }, { name: "basic-err-invalid-json", json: `nall`, expectedResult: testObject0Keys{}, err: true, errType: InvalidJSONError(""), }, { name: "basic-err-invalid-type", json: ``, expectedResult: testObject0Keys{}, err: true, errType: InvalidJSONError(""), }, { name: "basic-err", json: `{ "testStr": "hello world!", "testInt": 453q5, "testBool": trae, "testFloat32": 2q.345, "testFloat64": 12x3.677, "testInt8": 2s3, "testInt16": 1245, "testInt32": 4567q78, "testInt64": 14466e85358, "testUint8": 2s55, "testUint16": 345i5, "testUint32": 343q443, "testUint64": 5456657z57 }`, expectedResult: testObject0Keys{}, err: true, }, { name: "basic-err2", json: `{ "testStr": "hello world!", "testInt": 4535, "testBool": true, "testFloat32": 2.345, "testFloat64": 123.677, "testInt8": 23, "testInt16": 1245, "testInt32": 4567x78, "testInt64": 1446685358, "testUint8": 255, "testUint16": 3455, "testUint32": 343443, "testUint64": 545665757 }`, expectedResult: testObject0Keys{}, err: true, }, { name: "basic-err-float32", json: `{ "testStr": "hello world!", "testInt": 4535, "testBool": true, "testFloat32": 2q.345, "testFloat64": 123.677, "testInt8": 23, "testInt16": 1245, "testInt32": 456778, "testInt64": 1446685358, "testUint8": 255, "testUint16": 3455, "testUint32": 343443, "testUint64": 545665757 }`, expectedResult: testObject0Keys{}, err: true, }, { name: "basic-err-float64", json: `{ "testStr": "hello world!", "testInt": 4535, "testBool": true, "testFloat32": 2.345, "testFloat64": 1x23.677, "testInt8": 23, "testInt16": 1245, "testInt32": 456778, "testInt64": 1446685358, "testUint8": 255, "testUint16": 3455, "testUint32": 343443, "testUint64": 545665757 }`, expectedResult: testObject0Keys{}, err: true, }, { name: "basic-err3", json: `{ "testStr": "hello world!", "testInt": 4535, "testBool": true, "testFloat32": 2.345, "testFloat64": 123.677, "testInt8": 2q3, "testInt16": 1245, "testInt32": 456778, "testInt64": 1446685358, "testUint8": 255, "testUint16": 3455, "testUint32": 343443, "testUint64": 545665757 }`, expectedResult: testObject0Keys{}, err: true, }, { name: "basic-err-int16", json: `{ "testStr": "hello world!", "testInt": 4535, "testBool": true, "testFloat32": 2.345, "testFloat64": 123.677, "testInt8": 23, "testInt16": 1x245, "testInt32": 456778, "testInt64": 1446685358, "testUint8": 255, "testUint16": 3455, "testUint32": 343443, "testUint64": 545665757 }`, expectedResult: testObject0Keys{}, err: true, }, { name: "basic-err-int64", json: `{ "testStr": "hello world!", "testInt": 4535, "testBool": true, "testFloat32": 2.345, "testFloat64": 123.677, "testInt8": 23, "testInt16": 1245, "testInt32": 456778, "testInt64": 1446q685358, "testUint8": 255, "testUint16": 3455, "testUint32": 343443, "testUint64": 545665757 }`, expectedResult: testObject0Keys{}, err: true, }, { name: "basic-err-uint8", json: `{ "testStr": "hello world!", "testInt": 4535, "testBool": true, "testFloat32": 2.345, "testFloat64": 123.677, "testInt8": 23, "testInt16": 1245, "testInt32": 456778, "testInt64": 1446685358, "testUint8": 2x55, "testUint16": 3455, "testUint32": 343443, "testUint64": 545665757 }`, expectedResult: testObject0Keys{}, err: true, }, { name: "basic-err-uint16", json: `{ "testStr": "hello world!", "testInt": 4535, "testBool": true, "testFloat32": 2.345, "testFloat64": 123.677, "testInt8": 23, "testInt16": 1245, "testInt32": 456778, "testInt64": 1446685358, "testUint8": 255, "testUint16": 3x455, "testUint32": 343443, "testUint64": 545665757 }`, expectedResult: testObject0Keys{}, err: true, }, { name: "basic-err-uint32", json: `{ "testStr": "hello world!", "testInt": 4535, "testBool": true, "testFloat32": 2.345, "testFloat64": 123.677, "testInt8": 23, "testInt16": 1245, "testInt32": 456778, "testInt64": 1446685358, "testUint8": 255, "testUint16": 3455, "testUint32": 3x43443, "testUint64": 545665757 }`, expectedResult: testObject0Keys{}, err: true, }, { name: "basic-err-uint64", json: `{ "testStr": "hello world!", "testInt": 4535, "testBool": true, "testFloat32": 2.345, "testFloat64": 123.677, "testInt8": 23, "testInt16": 1245, "testInt32": 456778, "testInt64": 1446685358, "testUint8": 255, "testUint16": 3455, "testUint32": 343443, "testUint64": 5456x65757 }`, expectedResult: testObject0Keys{}, err: true, }, { name: "basic-skip-data", json: `{ "testStr": "hello world!", "testInt": 4535, "testBool": true, "testFloat32": 2.345, "testFloat64": 123.677, "testInt8": 23, "skipObject": { "escapedString": "string with escaped \\n new line" }, "testInt16": 1245, "testInt32": 456778, "testInt64": 1446685358, "testUint8": 255, "skipArray": [[],[],{}], "testUint16": 3455, "skipBool": true, "skipNull": null, "testUint32": 343443, "testUint64": 545665757, "skipString": "skipping string with escaped \\n new line", "skipInt": 3, }`, expectedResult: testObject0Keys{ testStr: "hello world!", testInt: 4535, testBool: true, testFloat32: 2.345, testFloat64: 123.677, testInt8: 23, testInt16: 1245, testInt32: 456778, testInt64: 1446685358, testUint8: 255, testUint16: 3455, testUint32: 343443, testUint64: 545665757, }, err: false, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { s := testObject0Keys{} dec := BorrowDecoder(strings.NewReader(testCase.json)) defer dec.Release() err := dec.Decode(&s) if testCase.err { t.Log(err) assert.NotNil(t, err, "err should not be nil") if testCase.errType != nil { assert.IsType(t, testCase.errType, err, "err should be of the given type") } return } assert.Nil(t, err, "err should be nil") if !testCase.skipCheckResult { assert.Equal(t, testCase.expectedResult, s, "value at given index should be the same as expected results") } }) } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { s := testObject0Keys{} err := UnmarshalJSONObject([]byte(testCase.json), &s) if testCase.err { t.Log(err) assert.NotNil(t, err, "err should not be nil") if testCase.errType != nil { assert.IsType(t, testCase.errType, err, "err should be of the given type") } return } assert.Nil(t, err, "err should be nil") if !testCase.skipCheckResult { assert.Equal(t, testCase.expectedResult, s, "value at given index should be the same as expected results") } }) } } type ObjectNull struct { SubObject *ObjectNull SubArray *testSliceBools } func (o *ObjectNull) UnmarshalJSONObject(dec *Decoder, k string) error { switch k { case "subobject": return dec.ObjectNull(&o.SubObject) case "subarray": return dec.AddArrayNull(&o.SubArray) } return nil } func (o *ObjectNull) NKeys() int { return 2 } type ObjectNullZeroNKeys struct { SubObject *ObjectNullZeroNKeys SubArray *testSliceBools } func (o *ObjectNullZeroNKeys) UnmarshalJSONObject(dec *Decoder, k string) error { switch k { case "subobject": return dec.AddObjectNull(&o.SubObject) case "subarray": return dec.AddArrayNull(&o.SubArray) } return nil } func (o *ObjectNullZeroNKeys) NKeys() int { return 0 } func TestDecodeObjectNull(t *testing.T) { t.Run("sub obj should not be nil", func(t *testing.T) { var o = &ObjectNull{} var err = UnmarshalJSONObject([]byte(`{"subobject": {},"subarray":[true]}`), o) assert.Nil(t, err) assert.NotNil(t, o.SubObject) assert.NotNil(t, o.SubArray) }) t.Run("sub obj and sub array should be nil", func(t *testing.T) { var o = &ObjectNull{} var err = UnmarshalJSONObject([]byte(`{"subobject": null,"subarray": null}`), o) assert.Nil(t, err) assert.Nil(t, o.SubObject) assert.Nil(t, o.SubArray) }) t.Run( "sub obj should not be be nil", func(t *testing.T) { var o = &ObjectNull{} var err = UnmarshalJSONObject([]byte(`{"subobject":{"subobject":{}}}`), DecodeObjectFunc(func(dec *Decoder, k string) error { return dec.ObjectNull(&o.SubObject) })) assert.Nil(t, err) assert.NotNil(t, o.SubObject) }, ) t.Run( "sub obj should be nil", func(t *testing.T) { var o = &ObjectNull{} var err = UnmarshalJSONObject([]byte(`{"subobject":null}`), DecodeObjectFunc(func(dec *Decoder, k string) error { return dec.ObjectNull(&o.SubObject) })) assert.Nil(t, err) assert.Nil(t, o.SubObject) }, ) t.Run( "skip data", func(t *testing.T) { var o = &ObjectNull{} var err = UnmarshalJSONObject([]byte(`{ "subobject": { "subobject": {}, "subarray": [], "subarray": [], "skipped": "" } }`), DecodeObjectFunc(func(dec *Decoder, k string) error { return dec.ObjectNull(&o.SubObject) })) assert.Nil(t, err) assert.NotNil(t, o.SubObject) assert.Nil(t, o.SubArray) }, ) t.Run( "skip data not child", func(t *testing.T) { var o = &ObjectNull{} var dec = NewDecoder(strings.NewReader(`{ "subobject": {}, "subarray": [], "subarray": [], "skipped": "" }`)) var _, err = dec.decodeObjectNull(&o) assert.Nil(t, err) assert.NotNil(t, o.SubObject) }, ) t.Run( "err empty json", func(t *testing.T) { var o = &ObjectNull{} var dec = NewDecoder(strings.NewReader(``)) var _, err = dec.decodeObjectNull(&o) assert.NotNil(t, err) }, ) t.Run( "should return an error as type is not ptr", func(t *testing.T) { var err = UnmarshalJSONObject([]byte(`{"key":{}}`), DecodeObjectFunc(func(dec *Decoder, k string) error { return dec.ObjectNull("") })) assert.NotNil(t, err) assert.Equal(t, ErrUnmarshalPtrExpected, err) }, ) t.Run( "should return an error as type is not ptr", func(t *testing.T) { var err = UnmarshalJSONObject([]byte(`{"key":[]}`), DecodeObjectFunc(func(dec *Decoder, k string) error { return dec.ArrayNull("") })) assert.NotNil(t, err) assert.Equal(t, ErrUnmarshalPtrExpected, err) }, ) t.Run( "should return an error as type is not ptr to UnmarshalerJSONObject", func(t *testing.T) { var err = UnmarshalJSONObject([]byte(`{"key":{}}`), DecodeObjectFunc(func(dec *Decoder, k string) error { var strPtr = new(string) return dec.ObjectNull(&strPtr) })) assert.NotNil(t, err) assert.IsType(t, InvalidUnmarshalError(""), err) }, ) t.Run( "should return an error as type is not ptr to UnmarshalerJSONObject", func(t *testing.T) { var err = UnmarshalJSONObject([]byte(`{"key":[]}`), DecodeObjectFunc(func(dec *Decoder, k string) error { var strPtr = new(string) return dec.ArrayNull(&strPtr) })) assert.NotNil(t, err) assert.IsType(t, InvalidUnmarshalError(""), err) }, ) t.Run( "should return an error as type is not ptr to UnmarshalerJSONObject", func(t *testing.T) { var err = UnmarshalJSONObject([]byte(`{"key":{}}`), DecodeObjectFunc(func(dec *Decoder, k string) error { var strPtr = new(string) return dec.ArrayNull(&strPtr) })) assert.NotNil(t, err) assert.IsType(t, InvalidUnmarshalError(""), err) }, ) t.Run( "should return an error as type is not ptr to UnmarshalerJSONObject", func(t *testing.T) { var err = UnmarshalJSONObject([]byte(`{"key":"`), DecodeObjectFunc(func(dec *Decoder, k string) error { var strPtr = new(string) return dec.ArrayNull(&strPtr) })) assert.NotNil(t, err) assert.IsType(t, InvalidJSONError(""), err) }, ) t.Run( "skip data", func(t *testing.T) { var err = UnmarshalJSONObject([]byte(`{"key": ""}`), DecodeObjectFunc(func(dec *Decoder, k string) error { var strPtr = new(string) return dec.ObjectNull(&strPtr) })) assert.NotNil(t, err) assert.IsType(t, InvalidUnmarshalError(""), err) }, ) t.Run( "invalid JSON for object", func(t *testing.T) { var o = &ObjectNull{} var err = UnmarshalJSONObject([]byte(`{"subobject":{"subobject":{"a":a}`), DecodeObjectFunc(func(dec *Decoder, k string) error { return dec.ObjectNull(&o.SubObject) })) assert.NotNil(t, err) assert.IsType(t, InvalidJSONError(""), err) }, ) t.Run( "invalid JSON for object", func(t *testing.T) { var o = &ObjectNull{} var err = UnmarshalJSONObject([]byte(`{"subobject":{"subobject":a}`), DecodeObjectFunc(func(dec *Decoder, k string) error { return dec.ObjectNull(&o.SubObject) })) assert.NotNil(t, err) assert.IsType(t, InvalidJSONError(""), err) }, ) t.Run( "invalid JSON for object", func(t *testing.T) { var o = &ObjectNull{} var err = UnmarshalJSONObject([]byte(`{"subobject":{"subobject":{"sub}}`), DecodeObjectFunc(func(dec *Decoder, k string) error { return dec.ObjectNull(&o.SubObject) })) assert.NotNil(t, err) assert.IsType(t, InvalidJSONError(""), err) }, ) t.Run( "invalid JSON for object", func(t *testing.T) { var o = &testSliceBools{} var err = UnmarshalJSONObject([]byte(`{"subobject":a`), DecodeObjectFunc(func(dec *Decoder, k string) error { return dec.ArrayNull(&o) })) assert.NotNil(t, err) assert.IsType(t, InvalidJSONError(""), err) }, ) t.Run( "invalid JSON for object", func(t *testing.T) { var err = UnmarshalJSONObject([]byte(`{"key":a`), DecodeObjectFunc(func(dec *Decoder, k string) error { var strPtr = new(string) return dec.ObjectNull(&strPtr) })) assert.NotNil(t, err) assert.IsType(t, InvalidJSONError(""), err) }, ) t.Run( "invalid JSON for object", func(t *testing.T) { var err = UnmarshalJSONObject([]byte(`{"subobject": {},"}`), DecodeObjectFunc(func(dec *Decoder, k string) error { var o = &ObjectNull{} return dec.ObjectNull(&o) })) assert.NotNil(t, err) assert.IsType(t, InvalidJSONError(""), err) }, ) t.Run( "invalid JSON for object", func(t *testing.T) { var o = &ObjectNull{} var err = UnmarshalJSONObject([]byte(`{"subobject": a`), o) assert.NotNil(t, err) assert.IsType(t, InvalidJSONError(""), err) }, ) t.Run( "invalid JSON for object", func(t *testing.T) { var o = &ObjectNull{} var err = UnmarshalJSONObject([]byte(`{"subobject": na`), o) assert.NotNil(t, err) assert.IsType(t, InvalidJSONError(""), err) }, ) t.Run( "zero nkeys, no error, two keys", func(t *testing.T) { var o = &ObjectNullZeroNKeys{} var err = UnmarshalJSONObject([]byte(`{ "subobject": { "subobject": { "subobject":{} }, "subarray": [] } }`), DecodeObjectFunc(func(dec *Decoder, k string) error { return dec.ObjectNull(&o.SubObject) })) assert.Nil(t, err) }, ) t.Run( "zero nkeys, no error, two keys, skip data", func(t *testing.T) { var o = &ObjectNullZeroNKeys{} var err = UnmarshalJSONObject([]byte(`{ "subobject": { "subobject": { "subobject":{} }, "subarray": [], "skipped": 1 } }`), DecodeObjectFunc(func(dec *Decoder, k string) error { return dec.ObjectNull(&o.SubObject) })) assert.Nil(t, err) }, ) t.Run( "zero nkeys, error skip data", func(t *testing.T) { var o = &ObjectNullZeroNKeys{} var err = UnmarshalJSONObject([]byte(`{ "subobject": { "subobject": { "subobject":{} }, "subarray": [], "skippedInvalid": "q } }`), DecodeObjectFunc(func(dec *Decoder, k string) error { return dec.ObjectNull(&o.SubObject) })) assert.NotNil(t, err) assert.IsType(t, InvalidJSONError(""), err) }, ) t.Run( "zero nkeys, error invalid json in keys", func(t *testing.T) { var o = &ObjectNullZeroNKeys{} var err = UnmarshalJSONObject([]byte(`{ "subobject": { "subobj } }`), DecodeObjectFunc(func(dec *Decoder, k string) error { return dec.ObjectNull(&o.SubObject) })) assert.NotNil(t, err) assert.IsType(t, InvalidJSONError(""), err) }, ) t.Run( "zero nkeys, error invalid json, sub object", func(t *testing.T) { var o = &ObjectNullZeroNKeys{} var err = UnmarshalJSONObject([]byte(`{ "subobject": { "subobject": { "subobj } } }`), DecodeObjectFunc(func(dec *Decoder, k string) error { return dec.ObjectNull(&o.SubObject) })) assert.NotNil(t, err) assert.IsType(t, InvalidJSONError(""), err) }, ) } func TestDecodeObjectComplex(t *testing.T) { testCases := []struct { name string json string expectedResult testObjectComplex err bool errType interface{} skipCheckResult bool }{ { name: "basic", json: `{ "testSubObject": {}, "testSubSliceInts": [1,2] }`, expectedResult: testObjectComplex{ testSubObject: &testObject{}, testSubSliceInts: &testSliceInts{1, 2}, }, err: false, }, { name: "complex", json: `{ "testSubObject": { "testStr": "some string", "testInt":124465, "testUint16":120, "testUint8":15, "testInt16":-135, "testInt8":-23 }, "testSubSliceInts": [1,2,3,4,5], "testStr": "some \n string" }`, expectedResult: testObjectComplex{ testSubObject: &testObject{ testStr: "some string", testInt: 124465, testUint16: 120, testUint8: 15, testInt16: -135, testInt8: -23, }, testSubSliceInts: &testSliceInts{1, 2, 3, 4, 5}, testStr: "some \n string", }, err: false, }, { name: "complex-json-err", json: `{"testSubObject":{"testStr":"some string,"testInt":124465,"testUint16":120, "testUint8":15,"testInt16":-135,"testInt8":-23},"testSubSliceInts":[1,2],"testStr":"some \n string"}`, expectedResult: testObjectComplex{ testSubObject: &testObject{}, }, err: true, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { s := testObjectComplex{ testSubObject: &testObject{}, testSubSliceInts: &testSliceInts{}, } dec := BorrowDecoder(strings.NewReader(testCase.json)) defer dec.Release() err := dec.Decode(&s) if testCase.err { t.Log(err) assert.NotNil(t, err, "err should not be nil") if testCase.errType != nil { assert.IsType(t, testCase.errType, err, "err should be of the given type") } return } assert.Nil(t, err, "err should be nil") if !testCase.skipCheckResult { assert.Equal(t, testCase.expectedResult, s, "value at given index should be the same as expected results") } }) } } func assertResult(t *testing.T, v *TestObj, err error) { assert.Nil(t, err, "Err must be nil") assert.Equal(t, 245, v.test, "v.test must be equal to 245") assert.Equal(t, 246, v.test2, "v.test2 must be equal to 246") assert.Equal(t, "string", v.test3, "v.test3 must be equal to 'string'") assert.Equal(t, "complex string with spaces and some slashes\"", v.test4, "v.test4 must be equal to 'string'") assert.Equal(t, -1.15657654376543, v.test5, "v.test5 must be equal to 1.15") assert.Len(t, v.testArr, 2, "v.testArr must be of len 2") assert.Equal(t, 121, v.testSubObj.test3, "v.testSubObj.test3 must be equal to 121") assert.Equal(t, 122, v.testSubObj.test4, "v.testSubObj.test4 must be equal to 122") assert.Equal(t, "string", v.testSubObj.test5, "v.testSubObj.test5 must be equal to 'string'") assert.Equal(t, 150, v.testSubObj.testSubSubObj.test3, "v.testSubObj.testSubSubObj.test3 must be equal to 150") assert.Equal(t, 150, v.testSubObj.testSubSubObj2.test3, "v.testSubObj.testSubSubObj2.test3 must be equal to 150") assert.Equal(t, 122, v.testSubObj2.test3, "v.testSubObj2.test3 must be equal to 121") assert.Equal(t, 123, v.testSubObj2.test4, "v.testSubObj2.test4 must be equal to 122") assert.Equal(t, "string", v.testSubObj2.test5, "v.testSubObj2.test5 must be equal to 'string'") assert.Equal(t, 151, v.testSubObj2.testSubSubObj.test3, "v.testSubObj2.testSubSubObj.test must be equal to 150") } func TestDecoderObject(t *testing.T) { json := []byte(`{ "test": 245, "test2": 246, "test3": "string", "test4": "complex string with spaces and some slashes\"", "test5": -1.15657654376543, "testNull": null, "testArr": [ { "test": 245, "test2": 246 }, { "test": 245, "test2": 246 } ], "testSubObj": { "test": 121, "test2": 122, "testNull": null, "testSubSubObj": { "test": 150, "testNull": null }, "testSubSubObj2": { "test": 150 }, "test3": "string" "testNull": null, }, "testSubObj2": { "test": 122, "test3": "string" "testSubSubObj": { "test": 151 }, "test2": 123 } }`) v := &TestObj{} err := Unmarshal(json, v) assertResult(t, v, err) } func TestDecodeObjectJSONNull(t *testing.T) { json := []byte(`null`) v := &TestObj{} err := Unmarshal(json, v) assert.Nil(t, err, "Err must be nil") assert.Equal(t, v.test, 0, "v.test must be 0 val") } var jsonComplex = []byte(`{ "test": "{\"test\":\"1\",\"test1\":2}", "test2\n": "\\\\\\\\\n", "testArrSkip": ["testString with escaped \\\" quotes"], "testSkipString": "skip \\ string with \n escaped char \" ", "testSkipObject": { "testSkipSubObj": { "test": "test" } }, "testSkipNumber": 123.23, "testSkipNumber2": 123.23 , "testBool": true, "testSkipBoolTrue": true, "testSkipBoolFalse": false, "testSkipBoolNull": null, "testSub": { "test": "{\"test\":\"1\",\"test1\":2}", "test2\n": "[1,2,3]", "test3": 1, "testObjSkip": { "test": "test string with escaped \" quotes" }, "testStrSkip" : "test" }, "testBoolSkip": false, "testObjInvalidType": "somestring", "testArrSkip2": [[],["someString"]], "test3": 1 }`) type jsonObjectComplex struct { Test string Test2 string Test3 int Test4 bool testSub *jsonObjectComplex testObjInvalidType *jsonObjectComplex } func (j *jsonObjectComplex) UnmarshalJSONObject(dec *Decoder, key string) error { switch key { case "test": return dec.AddString(&j.Test) case "test2\n": return dec.AddString(&j.Test2) case "test3": return dec.AddInt(&j.Test3) case "testBool": return dec.AddBool(&j.Test4) case "testSub": j.testSub = &jsonObjectComplex{} return dec.AddObject(j.testSub) case "testObjInvalidType": j.testObjInvalidType = &jsonObjectComplex{} return dec.AddObject(j.testObjInvalidType) } return nil } func (j *jsonObjectComplex) NKeys() int { return 6 } func TestDecodeObjComplex(t *testing.T) { result := jsonObjectComplex{} err := UnmarshalJSONObject(jsonComplex, &result) assert.NotNil(t, err, "err should not be as invalid type as been encountered nil") assert.Equal(t, `Cannot unmarshal JSON to type '*gojay.jsonObjectComplex'`, err.Error(), "err should not be as invalid type as been encountered nil") assert.Equal(t, `{"test":"1","test1":2}`, result.Test, "result.Test is not expected value") assert.Equal(t, "\\\\\\\\\n", result.Test2, "result.Test2 is not expected value") assert.Equal(t, 1, result.Test3, "result.test3 is not expected value") assert.Equal(t, `{"test":"1","test1":2}`, result.testSub.Test, "result.testSub.test is not expected value") assert.Equal(t, `[1,2,3]`, result.testSub.Test2, "result.testSub.test2 is not expected value") assert.Equal(t, 1, result.testSub.Test3, "result.testSub.test3 is not expected value") assert.Equal(t, true, result.Test4, "result.Test4 is not expected value, should be true") } type jsonDecodePartial struct { Test string Test2 string } func (j *jsonDecodePartial) UnmarshalJSONObject(dec *Decoder, key string) error { switch key { case "test": return dec.AddString(&j.Test) case `test2`: return dec.AddString(&j.Test2) } return nil } func (j *jsonDecodePartial) NKeys() int { return 2 } func TestDecodeObjectPartial(t *testing.T) { result := jsonDecodePartial{} dec := NewDecoder(nil) dec.data = []byte(`{ "test": "test", "test2": "test", "testArrSkip": ["test"], "testSkipString": "test", "testSkipNumber": 123.23 }`) dec.length = len(dec.data) err := dec.DecodeObject(&result) assert.Nil(t, err, "err should be nil") assert.NotEqual(t, len(dec.data), dec.cursor) } func TestDecoderObjectInvalidJSON(t *testing.T) { result := jsonDecodePartial{} dec := NewDecoder(nil) dec.data = []byte(`{ "test2": "test", "testArrSkip": ["test"], "testSkipString": "testInvalidJSON\\\\ }`) dec.length = len(dec.data) err := dec.DecodeObject(&result) assert.NotNil(t, err, "Err must not be nil as JSON is invalid") assert.IsType(t, InvalidJSONError(""), err, "err message must be 'Invalid JSON'") } type myMap map[string]string func (m myMap) UnmarshalJSONObject(dec *Decoder, k string) error { str := "" err := dec.AddString(&str) if err != nil { return err } m[k] = str return nil } // return 0 to parse all keys func (m myMap) NKeys() int { return 0 } func TestDecoderObjectMap(t *testing.T) { json := `{ "test": "string", "test2": "string", "test3": "string", "test4": "string", "test5": "string", }` m := myMap(make(map[string]string)) dec := BorrowDecoder(strings.NewReader(json)) err := dec.Decode(m) assert.Nil(t, err, "err should be nil") assert.Len(t, m, 5, "len of m should be 5") } func TestDecoderObjectDecoderAPI(t *testing.T) { json := `{ "test": 245, "test2": 246, "test3": "string", "test4": "complex string with spaces and some slashes\"", "test5": -1.15657654376543, "testNull": null, "testArr": [ { "test": 245, "test2": 246 }, { "test": 245, "test2": 246 } ], "testSubObj": { "test": 121, "test2": 122, "testNull": null, "testSubSubObj": { "test": 150, "testNull": null }, "testSubSubObj2": { "test": 150 }, "test3": "string" "testNull": null, }, "testSubObj2": { "test": 122, "test3": "string" "testSubSubObj": { "test": 151 }, "test2": 123 } }` v := &TestObj{} dec := NewDecoder(strings.NewReader(json)) err := dec.DecodeObject(v) assertResult(t, v, err) } type ReadCloser struct { json []byte } func (r *ReadCloser) Read(b []byte) (int, error) { copy(b, r.json) return len(r.json), io.EOF } func TestDecoderObjectDecoderAPIReadCloser(t *testing.T) { readCloser := ReadCloser{ json: []byte(`{ "test": "string", "test2": "string", "test3": "string", "test4": "string", "test5": "string", }`), } m := myMap(make(map[string]string)) dec := NewDecoder(&readCloser) err := dec.DecodeObject(m) assert.Nil(t, err, "err should be nil") assert.Len(t, m, 5, "len of m should be 5") } func TestDecoderObjectDecoderAPIFuncReadCloser(t *testing.T) { readCloser := ReadCloser{ json: []byte(`{ "test": "string", "test2": "string", "test3": "string", "test4": "string", "test5": "string", }`), } m := myMap(make(map[string]string)) dec := NewDecoder(&readCloser) err := dec.DecodeObject(DecodeObjectFunc(func(dec *Decoder, k string) error { str := "" err := dec.AddString(&str) if err != nil { return err } m[k] = str return nil })) assert.Nil(t, err, "err should be nil") assert.Len(t, m, 5, "len of m should be 5") } func TestDecoderObjectDecoderInvalidJSONError(t *testing.T) { v := &TestObj{} dec := NewDecoder(strings.NewReader(`{"err:}`)) err := dec.DecodeObject(v) assert.NotNil(t, err, "Err must not be nil as JSON is invalid") assert.IsType(t, InvalidJSONError(""), err, "err message must be 'Invalid JSON'") } func TestDecoderObjectDecoderInvalidJSONError2(t *testing.T) { v := &TestSubObj{} dec := NewDecoder(strings.NewReader(`{"err:}`)) err := dec.DecodeObject(v) assert.NotNil(t, err, "Err must not be nil as JSON is invalid") assert.IsType(t, InvalidJSONError(""), err, "err message must be 'Invalid JSON'") } func TestDecoderObjectDecoderInvalidJSONError3(t *testing.T) { v := &TestSubObj{} dec := NewDecoder(strings.NewReader(`{"err":"test}`)) err := dec.DecodeObject(v) assert.NotNil(t, err, "Err must not be nil as JSON is invalid") assert.IsType(t, InvalidJSONError(""), err, "err message must be 'Invalid JSON'") } func TestDecoderObjectDecoderInvalidJSONError4(t *testing.T) { testArr := testSliceInts{} dec := NewDecoder(strings.NewReader(`hello`)) err := dec.DecodeArray(&testArr) assert.NotNil(t, err, "Err must not be nil as JSON is invalid") assert.IsType(t, InvalidJSONError(""), err, "err message must be 'Invalid JSON'") } func TestDecoderObjectPoolError(t *testing.T) { result := jsonDecodePartial{} dec := NewDecoder(nil) dec.Release() defer func() { err := recover() assert.NotNil(t, err, "err shouldnt be nil") assert.IsType(t, InvalidUsagePooledDecoderError(""), err, "err should be of type InvalidUsagePooledDecoderError") }() _ = dec.DecodeObject(&result) assert.True(t, false, "should not be called as decoder should have panicked") } func TestNextKey(t *testing.T) { testCases := []struct { name string json string expectedValue string err bool }{ { name: "basic", json: `"key":"value"`, expectedValue: "key", }, { name: "basic-err", json: ``, expectedValue: "", err: true, }, { name: "basic-err2", json: `"key"`, expectedValue: "", err: true, }, { name: "basic-err3", json: `"key`, expectedValue: "", err: true, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { dec := BorrowDecoder(strings.NewReader(testCase.json)) s, _, err := dec.nextKey() if testCase.err { assert.NotNil(t, err, "err should not be nil") return } assert.Nil(t, err, "err should be nil") assert.Equal(t, testCase.expectedValue, s, fmt.Sprintf("s should be '%s'", testCase.expectedValue)) }) } } func TestSkipObject(t *testing.T) { testCases := []struct { name string json string err bool }{ { name: "basic", json: `"key":"value"}`, }, { name: "basic-escape-solidus", json: `"key":"value\/solidus"}`, }, { name: "basic-escaped", json: `"key":"value\\\\\\\" hello"}`, }, { name: "basic-escaped", json: `"key":"value\\\\\\\\"}`, }, { name: "basic-err", json: ``, err: true, }, { name: "basic-err2", json: `{"key":"value"`, err: true, }, { name: "basic-err2", json: `"key":"value\n"}`, err: false, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { dec := BorrowDecoder(strings.NewReader(testCase.json)) defer dec.Release() _, err := dec.skipObject() if testCase.err { assert.NotNil(t, err, "err should not be nil") return } assert.Nil(t, err, "err should be nil") }) } } func TestSkipData(t *testing.T) { testCases := []struct { name string err bool json string }{ { name: "skip-bool-false-err", json: `fulse`, err: true, }, { name: "skip-bool-true-err", json: `trou`, err: true, }, { name: "skip-bool-null-err", json: `nil`, err: true, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { dec := NewDecoder(strings.NewReader(testCase.json)) err := dec.skipData() if testCase.err { assert.NotNil(t, err, "err should not be nil") } else { assert.Nil(t, err, "err should be nil") } }) } t.Run("error-invalid-json", func(t *testing.T) { dec := NewDecoder(strings.NewReader("")) err := dec.skipData() assert.NotNil(t, err, "err should not be nil as data is empty") assert.IsType(t, InvalidJSONError(""), err, "err should of type InvalidJSONError") }) t.Run("skip-array-error-invalid-json", func(t *testing.T) { dec := NewDecoder(strings.NewReader("")) _, err := dec.skipArray() assert.NotNil(t, err, "err should not be nil as data is empty") assert.IsType(t, InvalidJSONError(""), err, "err should of type InvalidJSONError") }) } golang-github-francoispqt-gojay-1.2.13/decode_pool.go000066400000000000000000000024221365331665100225770ustar00rootroot00000000000000package gojay import ( "io" "sync" ) var decPool = sync.Pool{ New: newDecoderPool, } func init() { for i := 0; i < 32; i++ { decPool.Put(NewDecoder(nil)) } } // NewDecoder returns a new decoder. // It takes an io.Reader implementation as data input. func NewDecoder(r io.Reader) *Decoder { return &Decoder{ called: 0, cursor: 0, keysDone: 0, err: nil, r: r, data: make([]byte, 512), length: 0, isPooled: 0, } } func newDecoderPool() interface{} { return NewDecoder(nil) } // BorrowDecoder borrows a Decoder from the pool. // It takes an io.Reader implementation as data input. // // In order to benefit from the pool, a borrowed decoder must be released after usage. func BorrowDecoder(r io.Reader) *Decoder { return borrowDecoder(r, 512) } func borrowDecoder(r io.Reader, bufSize int) *Decoder { dec := decPool.Get().(*Decoder) dec.called = 0 dec.keysDone = 0 dec.cursor = 0 dec.err = nil dec.r = r dec.length = 0 dec.isPooled = 0 if bufSize > 0 { dec.data = make([]byte, bufSize) } return dec } // Release sends back a Decoder to the pool. // If a decoder is used after calling Release // a panic will be raised with an InvalidUsagePooledDecoderError error. func (dec *Decoder) Release() { dec.isPooled = 1 decPool.Put(dec) } golang-github-francoispqt-gojay-1.2.13/decode_pool_test.go000066400000000000000000000005561365331665100236440ustar00rootroot00000000000000package gojay import ( "testing" "github.com/stretchr/testify/assert" ) func TestDecoderBorrowFromPoolSetBuffSize(t *testing.T) { dec := borrowDecoder(nil, 512) assert.Len(t, dec.data, 512, "data buffer should be of len 512") } func TestDecoderNewPool(t *testing.T) { dec := newDecoderPool() assert.IsType(t, &Decoder{}, dec, "dec should be a *Decoder") } golang-github-francoispqt-gojay-1.2.13/decode_slice.go000066400000000000000000000040461365331665100227310ustar00rootroot00000000000000package gojay // AddSliceString unmarshals the next JSON array of strings to the given *[]string s func (dec *Decoder) AddSliceString(s *[]string) error { return dec.SliceString(s) } // SliceString unmarshals the next JSON array of strings to the given *[]string s func (dec *Decoder) SliceString(s *[]string) error { err := dec.Array(DecodeArrayFunc(func(dec *Decoder) error { var str string if err := dec.String(&str); err != nil { return err } *s = append(*s, str) return nil })) if err != nil { return err } return nil } // AddSliceInt unmarshals the next JSON array of integers to the given *[]int s func (dec *Decoder) AddSliceInt(s *[]int) error { return dec.SliceInt(s) } // SliceInt unmarshals the next JSON array of integers to the given *[]int s func (dec *Decoder) SliceInt(s *[]int) error { err := dec.Array(DecodeArrayFunc(func(dec *Decoder) error { var i int if err := dec.Int(&i); err != nil { return err } *s = append(*s, i) return nil })) if err != nil { return err } return nil } // AddFloat64 unmarshals the next JSON array of floats to the given *[]float64 s func (dec *Decoder) AddSliceFloat64(s *[]float64) error { return dec.SliceFloat64(s) } // SliceFloat64 unmarshals the next JSON array of floats to the given *[]float64 s func (dec *Decoder) SliceFloat64(s *[]float64) error { err := dec.Array(DecodeArrayFunc(func(dec *Decoder) error { var i float64 if err := dec.Float64(&i); err != nil { return err } *s = append(*s, i) return nil })) if err != nil { return err } return nil } // AddBool unmarshals the next JSON array of boolegers to the given *[]bool s func (dec *Decoder) AddSliceBool(s *[]bool) error { return dec.SliceBool(s) } // SliceBool unmarshals the next JSON array of boolegers to the given *[]bool s func (dec *Decoder) SliceBool(s *[]bool) error { err := dec.Array(DecodeArrayFunc(func(dec *Decoder) error { var b bool if err := dec.Bool(&b); err != nil { return err } *s = append(*s, b) return nil })) if err != nil { return err } return nil } golang-github-francoispqt-gojay-1.2.13/decode_slice_test.go000066400000000000000000000043361365331665100237720ustar00rootroot00000000000000package gojay import ( "strings" "testing" "github.com/stretchr/testify/require" ) type slicesTestObject struct { sliceString []string sliceInt []int sliceFloat64 []float64 sliceBool []bool } func (s *slicesTestObject) UnmarshalJSONObject(dec *Decoder, k string) error { switch k { case "sliceString": return dec.AddSliceString(&s.sliceString) case "sliceInt": return dec.AddSliceInt(&s.sliceInt) case "sliceFloat64": return dec.AddSliceFloat64(&s.sliceFloat64) case "sliceBool": return dec.AddSliceBool(&s.sliceBool) } return nil } func (s *slicesTestObject) NKeys() int { return 4 } func TestDecodeSlices(t *testing.T) { testCases := []struct { name string json string expectedResult slicesTestObject err bool }{ { name: "basic slice string", json: `{ "sliceString": ["foo","bar"] }`, expectedResult: slicesTestObject{ sliceString: []string{"foo", "bar"}, }, }, { name: "basic slice bool", json: `{ "sliceBool": [true,false] }`, expectedResult: slicesTestObject{ sliceBool: []bool{true, false}, }, }, { name: "basic slice int", json: `{ "sliceInt": [1,2,3] }`, expectedResult: slicesTestObject{ sliceInt: []int{1, 2, 3}, }, }, { name: "basic slice float64", json: `{ "sliceFloat64": [1.3,2.4,3.1] }`, expectedResult: slicesTestObject{ sliceFloat64: []float64{1.3, 2.4, 3.1}, }, }, { name: "err slice float64", json: `{ "sliceFloat64": [1.3",2.4,3.1] }`, err: true, }, { name: "err slice str", json: `{ "sliceString": [",""] }`, err: true, }, { name: "err slice int", json: `{ "sliceInt": [1t,2,3] }`, err: true, }, { name: "err slice bool", json: `{ "sliceBool": [truo,false] }`, err: true, }, } for _, testCase := range testCases { t.Run( testCase.name, func(t *testing.T) { dec := BorrowDecoder(strings.NewReader(testCase.json)) var o slicesTestObject err := dec.Decode(&o) if testCase.err { require.NotNil(t, err, "err should not be nil") return } require.Nil(t, err, "err should be nil") require.Equal(t, testCase.expectedResult, o) }, ) } } golang-github-francoispqt-gojay-1.2.13/decode_sqlnull.go000066400000000000000000000074551365331665100233330ustar00rootroot00000000000000package gojay import "database/sql" // DecodeSQLNullString decodes a sql.NullString func (dec *Decoder) DecodeSQLNullString(v *sql.NullString) error { if dec.isPooled == 1 { panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) } return dec.decodeSQLNullString(v) } func (dec *Decoder) decodeSQLNullString(v *sql.NullString) error { var str string if err := dec.decodeString(&str); err != nil { return err } v.String = str v.Valid = true return nil } // DecodeSQLNullInt64 decodes a sql.NullInt64 func (dec *Decoder) DecodeSQLNullInt64(v *sql.NullInt64) error { if dec.isPooled == 1 { panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) } return dec.decodeSQLNullInt64(v) } func (dec *Decoder) decodeSQLNullInt64(v *sql.NullInt64) error { var i int64 if err := dec.decodeInt64(&i); err != nil { return err } v.Int64 = i v.Valid = true return nil } // DecodeSQLNullFloat64 decodes a sql.NullString with the given format func (dec *Decoder) DecodeSQLNullFloat64(v *sql.NullFloat64) error { if dec.isPooled == 1 { panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) } return dec.decodeSQLNullFloat64(v) } func (dec *Decoder) decodeSQLNullFloat64(v *sql.NullFloat64) error { var i float64 if err := dec.decodeFloat64(&i); err != nil { return err } v.Float64 = i v.Valid = true return nil } // DecodeSQLNullBool decodes a sql.NullString with the given format func (dec *Decoder) DecodeSQLNullBool(v *sql.NullBool) error { if dec.isPooled == 1 { panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) } return dec.decodeSQLNullBool(v) } func (dec *Decoder) decodeSQLNullBool(v *sql.NullBool) error { var b bool if err := dec.decodeBool(&b); err != nil { return err } v.Bool = b v.Valid = true return nil } // Add Values functions // AddSQLNullString decodes the JSON value within an object or an array to qn *sql.NullString func (dec *Decoder) AddSQLNullString(v *sql.NullString) error { return dec.SQLNullString(v) } // SQLNullString decodes the JSON value within an object or an array to an *sql.NullString func (dec *Decoder) SQLNullString(v *sql.NullString) error { var b *string if err := dec.StringNull(&b); err != nil { return err } if b == nil { v.Valid = false } else { v.String = *b v.Valid = true } return nil } // AddSQLNullInt64 decodes the JSON value within an object or an array to qn *sql.NullInt64 func (dec *Decoder) AddSQLNullInt64(v *sql.NullInt64) error { return dec.SQLNullInt64(v) } // SQLNullInt64 decodes the JSON value within an object or an array to an *sql.NullInt64 func (dec *Decoder) SQLNullInt64(v *sql.NullInt64) error { var b *int64 if err := dec.Int64Null(&b); err != nil { return err } if b == nil { v.Valid = false } else { v.Int64 = *b v.Valid = true } return nil } // AddSQLNullFloat64 decodes the JSON value within an object or an array to qn *sql.NullFloat64 func (dec *Decoder) AddSQLNullFloat64(v *sql.NullFloat64) error { return dec.SQLNullFloat64(v) } // SQLNullFloat64 decodes the JSON value within an object or an array to an *sql.NullFloat64 func (dec *Decoder) SQLNullFloat64(v *sql.NullFloat64) error { var b *float64 if err := dec.Float64Null(&b); err != nil { return err } if b == nil { v.Valid = false } else { v.Float64 = *b v.Valid = true } return nil } // AddSQLNullBool decodes the JSON value within an object or an array to an *sql.NullBool func (dec *Decoder) AddSQLNullBool(v *sql.NullBool) error { return dec.SQLNullBool(v) } // SQLNullBool decodes the JSON value within an object or an array to an *sql.NullBool func (dec *Decoder) SQLNullBool(v *sql.NullBool) error { var b *bool if err := dec.BoolNull(&b); err != nil { return err } if b == nil { v.Valid = false } else { v.Bool = *b v.Valid = true } return nil } golang-github-francoispqt-gojay-1.2.13/decode_sqlnull_test.go000066400000000000000000000212441365331665100243620ustar00rootroot00000000000000package gojay import ( "database/sql" "strings" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestDecodeSQLNullString(t *testing.T) { testCases := []struct { name string json string expectedNullString sql.NullString err bool }{ { name: "basic", json: `"test"`, expectedNullString: sql.NullString{String: "test", Valid: true}, }, { name: "basic", json: `"test`, expectedNullString: sql.NullString{String: "test", Valid: true}, err: true, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { nullString := sql.NullString{} dec := NewDecoder(strings.NewReader(testCase.json)) err := dec.DecodeSQLNullString(&nullString) if testCase.err { assert.NotNil(t, err) } else { assert.Nil(t, err) assert.Equal(t, testCase.expectedNullString, nullString) } }) } t.Run( "should panic because decoder is pooled", func(t *testing.T) { dec := NewDecoder(nil) dec.Release() defer func() { err := recover() assert.NotNil(t, err, "err shouldnt be nil") assert.IsType(t, InvalidUsagePooledDecoderError(""), err, "err should be of type InvalidUsagePooledDecoderError") }() _ = dec.DecodeSQLNullString(&sql.NullString{}) assert.True(t, false, "should not be called as decoder should have panicked") }, ) } func TestDecodeSQLNullInt64(t *testing.T) { testCases := []struct { name string json string expectedNullInt64 sql.NullInt64 err bool }{ { name: "basic", json: `1`, expectedNullInt64: sql.NullInt64{Int64: 1, Valid: true}, }, { name: "basic", json: `"test`, expectedNullInt64: sql.NullInt64{Int64: 1, Valid: true}, err: true, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { nullInt64 := sql.NullInt64{} dec := NewDecoder(strings.NewReader(testCase.json)) err := dec.DecodeSQLNullInt64(&nullInt64) if testCase.err { assert.NotNil(t, err) } else { assert.Nil(t, err) assert.Equal(t, testCase.expectedNullInt64, nullInt64) } }) } t.Run( "should panic because decoder is pooled", func(t *testing.T) { dec := NewDecoder(nil) dec.Release() defer func() { err := recover() assert.NotNil(t, err, "err shouldnt be nil") assert.IsType(t, InvalidUsagePooledDecoderError(""), err, "err should be of type InvalidUsagePooledDecoderError") }() _ = dec.DecodeSQLNullInt64(&sql.NullInt64{}) assert.True(t, false, "should not be called as decoder should have panicked") }, ) } func TestDecodeSQLNullFloat64(t *testing.T) { testCases := []struct { name string json string expectedNullFloat64 sql.NullFloat64 err bool }{ { name: "basic", json: `1`, expectedNullFloat64: sql.NullFloat64{Float64: 1, Valid: true}, }, { name: "basic", json: `"test`, expectedNullFloat64: sql.NullFloat64{Float64: 1, Valid: true}, err: true, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { nullFloat64 := sql.NullFloat64{} dec := NewDecoder(strings.NewReader(testCase.json)) err := dec.DecodeSQLNullFloat64(&nullFloat64) if testCase.err { assert.NotNil(t, err) } else { assert.Nil(t, err) assert.Equal(t, testCase.expectedNullFloat64, nullFloat64) } }) } t.Run( "should panic because decoder is pooled", func(t *testing.T) { dec := NewDecoder(nil) dec.Release() defer func() { err := recover() assert.NotNil(t, err, "err shouldnt be nil") assert.IsType(t, InvalidUsagePooledDecoderError(""), err, "err should be of type InvalidUsagePooledDecoderError") }() _ = dec.DecodeSQLNullFloat64(&sql.NullFloat64{}) assert.True(t, false, "should not be called as decoder should have panicked") }, ) } func TestDecodeSQLNullBool(t *testing.T) { testCases := []struct { name string json string expectedNullBool sql.NullBool err bool }{ { name: "basic", json: `true`, expectedNullBool: sql.NullBool{Bool: true, Valid: true}, }, { name: "basic", json: `"&`, expectedNullBool: sql.NullBool{Bool: true, Valid: true}, err: true, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { nullBool := sql.NullBool{} dec := NewDecoder(strings.NewReader(testCase.json)) err := dec.DecodeSQLNullBool(&nullBool) if testCase.err { assert.NotNil(t, err) } else { assert.Nil(t, err) assert.Equal(t, testCase.expectedNullBool, nullBool) } }) } t.Run( "should panic because decoder is pooled", func(t *testing.T) { dec := NewDecoder(nil) dec.Release() defer func() { err := recover() assert.NotNil(t, err, "err shouldnt be nil") assert.IsType(t, InvalidUsagePooledDecoderError(""), err, "err should be of type InvalidUsagePooledDecoderError") }() _ = dec.DecodeSQLNullBool(&sql.NullBool{}) assert.True(t, false, "should not be called as decoder should have panicked") }, ) } type SQLDecodeObject struct { S sql.NullString F sql.NullFloat64 I sql.NullInt64 B sql.NullBool } func (s *SQLDecodeObject) UnmarshalJSONObject(dec *Decoder, k string) error { switch k { case "s": return dec.AddSQLNullString(&s.S) case "f": return dec.AddSQLNullFloat64(&s.F) case "i": return dec.AddSQLNullInt64(&s.I) case "b": return dec.AddSQLNullBool(&s.B) } return nil } func (s *SQLDecodeObject) NKeys() int { return 0 } func TestDecodeSQLNullKeys(t *testing.T) { var testCases = []struct { name string json string expectedResult *SQLDecodeObject err bool }{ { name: "basic all valid", json: `{ "s": "foo", "f": 0.3, "i": 3, "b": true }`, expectedResult: &SQLDecodeObject{ S: sql.NullString{ String: "foo", Valid: true, }, F: sql.NullFloat64{ Float64: 0.3, Valid: true, }, I: sql.NullInt64{ Int64: 3, Valid: true, }, B: sql.NullBool{ Bool: true, Valid: true, }, }, }, { name: "string not valid", json: `{ "s": null, "f": 0.3, "i": 3, "b": true }`, expectedResult: &SQLDecodeObject{ S: sql.NullString{ Valid: false, }, F: sql.NullFloat64{ Float64: 0.3, Valid: true, }, I: sql.NullInt64{ Int64: 3, Valid: true, }, B: sql.NullBool{ Bool: true, Valid: true, }, }, }, { name: "string not valid, int not valid", json: `{ "s": null, "f": 0.3, "i": null, "b": true }`, expectedResult: &SQLDecodeObject{ S: sql.NullString{ Valid: false, }, F: sql.NullFloat64{ Float64: 0.3, Valid: true, }, I: sql.NullInt64{ Valid: false, }, B: sql.NullBool{ Bool: true, Valid: true, }, }, }, { name: "keys absent", json: `{ "f": 0.3, "i": 3, "b": true }`, expectedResult: &SQLDecodeObject{ S: sql.NullString{ Valid: false, }, F: sql.NullFloat64{ Float64: 0.3, Valid: true, }, I: sql.NullInt64{ Valid: true, Int64: 3, }, B: sql.NullBool{ Bool: true, Valid: true, }, }, }, { name: "keys all null", json: `{ "s": null, "f": null, "i": null, "b": null }`, expectedResult: &SQLDecodeObject{ S: sql.NullString{ Valid: false, }, F: sql.NullFloat64{ Valid: false, }, I: sql.NullInt64{ Valid: false, }, B: sql.NullBool{ Valid: false, }, }, }, { name: "err string key", json: `{ "s": "`, err: true, }, { name: "err float key", json: `{ "s": null, "f": 1", "i": null, "b": null }`, err: true, }, { name: "err int key", json: `{ "s": null, "f": null, "i": 1", "b": null }`, err: true, }, { name: "err bool key", json: `{ "s": null, "f": null, "i": null, "b": tra }`, err: true, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { var o = &SQLDecodeObject{} var dec = NewDecoder(strings.NewReader(testCase.json)) var err = dec.Decode(o) if testCase.err { require.NotNil(t, err) return } require.Nil(t, err) require.Equal( t, testCase.expectedResult, o, ) }) } } golang-github-francoispqt-gojay-1.2.13/decode_stream.go000066400000000000000000000057521365331665100231320ustar00rootroot00000000000000package gojay import ( "sync" "time" ) // UnmarshalerStream is the interface to implement for a slice, an array or a slice // to decode a line delimited JSON to. type UnmarshalerStream interface { UnmarshalStream(*StreamDecoder) error } // Stream is a struct holding the Stream api var Stream = stream{} type stream struct{} // A StreamDecoder reads and decodes JSON values from an input stream. // // It implements conext.Context and provide a channel to notify interruption. type StreamDecoder struct { mux sync.RWMutex *Decoder done chan struct{} deadline *time.Time } // DecodeStream reads the next line delimited JSON-encoded value from the decoder's input (io.Reader) and stores it in the value pointed to by c. // // c must implement UnmarshalerStream. Ideally c is a channel. See example for implementation. // // See the documentation for Unmarshal for details about the conversion of JSON into a Go value. func (dec *StreamDecoder) DecodeStream(c UnmarshalerStream) error { if dec.isPooled == 1 { panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) } if dec.r == nil { dec.err = NoReaderError("No reader given to decode stream") close(dec.done) return dec.err } for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { switch dec.data[dec.cursor] { case ' ', '\n', '\t', '\r', ',': continue default: // char is not space start reading for dec.nextChar() != 0 { // calling unmarshal stream err := c.UnmarshalStream(dec) if err != nil { dec.err = err close(dec.done) return err } // garbage collects buffer // we don't want the buffer to grow extensively dec.data = dec.data[dec.cursor:] dec.length = dec.length - dec.cursor dec.cursor = 0 } // close the done channel to signal the end of the job close(dec.done) return nil } } close(dec.done) dec.mux.Lock() err := dec.raiseInvalidJSONErr(dec.cursor) dec.mux.Unlock() return err } // context.Context implementation // Done returns a channel that's closed when work is done. // It implements context.Context func (dec *StreamDecoder) Done() <-chan struct{} { return dec.done } // Deadline returns the time when work done on behalf of this context // should be canceled. Deadline returns ok==false when no deadline is // set. Successive calls to Deadline return the same results. func (dec *StreamDecoder) Deadline() (time.Time, bool) { if dec.deadline != nil { return *dec.deadline, true } return time.Time{}, false } // SetDeadline sets the deadline func (dec *StreamDecoder) SetDeadline(t time.Time) { dec.deadline = &t } // Err returns nil if Done is not yet closed. // If Done is closed, Err returns a non-nil error explaining why. // It implements context.Context func (dec *StreamDecoder) Err() error { select { case <-dec.done: dec.mux.RLock() defer dec.mux.RUnlock() return dec.err default: return nil } } // Value implements context.Context func (dec *StreamDecoder) Value(key interface{}) interface{} { return nil } golang-github-francoispqt-gojay-1.2.13/decode_stream_pool.go000066400000000000000000000027571365331665100241650ustar00rootroot00000000000000package gojay import ( "io" "sync" ) var streamDecPool = sync.Pool{ New: newStreamDecoderPool, } // NewDecoder returns a new StreamDecoder. // It takes an io.Reader implementation as data input. // It initiates the done channel returned by Done(). func (s stream) NewDecoder(r io.Reader) *StreamDecoder { dec := NewDecoder(r) streamDec := &StreamDecoder{ Decoder: dec, done: make(chan struct{}, 1), mux: sync.RWMutex{}, } return streamDec } func newStreamDecoderPool() interface{} { return Stream.NewDecoder(nil) } // BorrowDecoder borrows a StreamDecoder from the pool. // It takes an io.Reader implementation as data input. // It initiates the done channel returned by Done(). // // If no StreamEncoder is available in the pool, it returns a fresh one func (s stream) BorrowDecoder(r io.Reader) *StreamDecoder { return s.borrowDecoder(r, 512) } func (s stream) borrowDecoder(r io.Reader, bufSize int) *StreamDecoder { streamDec := streamDecPool.Get().(*StreamDecoder) streamDec.called = 0 streamDec.keysDone = 0 streamDec.cursor = 0 streamDec.err = nil streamDec.r = r streamDec.length = 0 streamDec.isPooled = 0 streamDec.done = make(chan struct{}, 1) if bufSize > 0 { streamDec.data = make([]byte, bufSize) } return streamDec } // Release sends back a Decoder to the pool. // If a decoder is used after calling Release // a panic will be raised with an InvalidUsagePooledDecoderError error. func (dec *StreamDecoder) Release() { dec.isPooled = 1 streamDecPool.Put(dec) } golang-github-francoispqt-gojay-1.2.13/decode_stream_pool_test.go000066400000000000000000000035271365331665100252200ustar00rootroot00000000000000package gojay import ( "testing" "github.com/stretchr/testify/assert" ) func TestDecodeStreamDecodePooledDecoderError(t *testing.T) { // we override the pool chan dec := Stream.NewDecoder(nil) dec.Release() defer func() { err := recover() assert.NotNil(t, err, "err shouldnt be nil") assert.IsType(t, InvalidUsagePooledDecoderError(""), err, "err should be of type InvalidUsagePooledDecoderError") }() var v = 0 dec.Decode(&v) // make sure it fails if this is called assert.True(t, false, "should not be called as decoder should have panicked") } func TestDecodeStreamDecodePooledDecoderError1(t *testing.T) { // we override the pool chan dec := Stream.NewDecoder(nil) dec.Release() defer func() { err := recover() assert.NotNil(t, err, "err shouldnt be nil") assert.IsType(t, InvalidUsagePooledDecoderError(""), err, "err should be of type InvalidUsagePooledDecoderError") }() var v = testSliceStrings{} dec.DecodeArray(&v) // make sure they are the same assert.True(t, false, "should not be called as decoder should have panicked") } func TestDecodeStreamDecodePooledDecoderError2(t *testing.T) { // we override the pool chan dec := Stream.NewDecoder(nil) dec.Release() defer func() { err := recover() assert.NotNil(t, err, "err shouldnt be nil") assert.IsType(t, InvalidUsagePooledDecoderError(""), err, "err should be of type InvalidUsagePooledDecoderError") assert.Equal(t, "Invalid usage of pooled decoder", err.(InvalidUsagePooledDecoderError).Error(), "err should be of type InvalidUsagePooledDecoderError") }() var v = TestObj{} dec.DecodeObject(&v) // make sure they are the same assert.True(t, false, "should not be called as decoder should have panicked") } func TestStreamDecoderNewPool(t *testing.T) { dec := newStreamDecoderPool() assert.IsType(t, &StreamDecoder{}, dec, "dec should be a *StreamDecoder") } golang-github-francoispqt-gojay-1.2.13/decode_stream_test.go000066400000000000000000000331611365331665100241640ustar00rootroot00000000000000package gojay import ( "context" "errors" "io" "testing" "time" "github.com/stretchr/testify/assert" ) // Basic Behaviour Tests // func TestDecoderImplementsContext(t *testing.T) { var dec interface{} = &StreamDecoder{} _ = dec.(context.Context) } func TestDecodeStreamNoReader(t *testing.T) { dec := Stream.NewDecoder(nil) dec.done = make(chan struct{}, 1) testChan := ChannelStreamObjects(make(chan *TestObj)) go dec.DecodeStream(&testChan) select { case <-dec.Done(): assert.NotNil(t, dec.Err(), "dec.Err() should not be nil") assert.Equal(t, "No reader given to decode stream", dec.Err().Error(), "dec.Err().Error() should not be 'No reader given to decode stream'") case <-testChan: assert.True(t, false, "should not be called as decoder should not return error right away") } } // Table Tests // Objects type StreamTestObject struct { name string streamReader *StreamReader expectations func(error, []*TestObj, *testing.T) } func TestStreamDecodingObjectsParallel(t *testing.T) { var tests = []StreamTestObject{ { name: "Stream objects", streamReader: &StreamReader{ readChan: make(chan string), done: make(chan struct{}), data: ` {"test":246,"test2":-246,"test3":"string"} {"test":247,"test2":248,"test3":"string"} {"test":777,"test2":456,"test3":"string"} {"test":777,"test2":456,"test3":"string"} {"test":777,"test2":456,"test3":"string"} {"test":777,"test2":456,"test3":"string"} `, }, expectations: func(err error, result []*TestObj, t *testing.T) { assert.Nil(t, err, "err should be nil") assert.Equal(t, 246, result[0].test, "v[0].test should be equal to 246") assert.Equal(t, -246, result[0].test2, "v[0].test2 should be equal to -247") assert.Equal(t, "string", result[0].test3, "v[0].test3 should be equal to \"string\"") assert.Equal(t, 247, result[1].test, "result[1].test should be equal to 246") assert.Equal(t, 248, result[1].test2, "result[1].test2 should be equal to 248") assert.Equal(t, "string", result[1].test3, "result[1].test3 should be equal to \"string\"") assert.Equal(t, 777, result[2].test, "result[2].test should be equal to 777") assert.Equal(t, 456, result[2].test2, "result[2].test2 should be equal to 456") assert.Equal(t, "string", result[2].test3, "result[2].test3 should be equal to \"string\"") assert.Equal(t, 777, result[3].test, "result[3].test should be equal to 777") assert.Equal(t, 456, result[3].test2, "result[3].test2 should be equal to 456") assert.Equal(t, "string", result[3].test3, "result[3].test3 should be equal to \"string\"") assert.Equal(t, 777, result[4].test, "result[4].test should be equal to 777") assert.Equal(t, 456, result[4].test2, "result[4].test2 should be equal to 456") assert.Equal(t, "string", result[4].test3, "result[4].test3 should be equal to \"string\"") assert.Equal(t, 777, result[5].test, "result[5].test should be equal to 777") assert.Equal(t, 456, result[5].test2, "result[5].test2 should be equal to 456") assert.Equal(t, "string", result[5].test3, "result[5].test3 should be equal to \"string\"") }, }, { name: "Stream test objects with null values", streamReader: &StreamReader{ readChan: make(chan string), done: make(chan struct{}), data: ` {"test":246,"test2":-246,"test3":"string"} {"test":247,"test2":248,"test3":"string"} null {"test":777,"test2":456,"test3":"string"} {"test":777,"test2":456,"test3":"string"} {"test":777,"test2":456,"test3":"string"} `, }, expectations: func(err error, result []*TestObj, t *testing.T) { assert.Nil(t, err, "err should be nil") assert.Equal(t, 246, result[0].test, "v[0].test should be equal to 246") assert.Equal(t, -246, result[0].test2, "v[0].test2 should be equal to -247") assert.Equal(t, "string", result[0].test3, "v[0].test3 should be equal to \"string\"") assert.Equal(t, 247, result[1].test, "result[1].test should be equal to 246") assert.Equal(t, 248, result[1].test2, "result[1].test2 should be equal to 248") assert.Equal(t, "string", result[1].test3, "result[1].test3 should be equal to \"string\"") assert.Equal(t, 0, result[2].test, "result[2].test should be equal to 0 as input is null") assert.Equal(t, 0, result[2].test2, "result[2].test2 should be equal to 0 as input is null") assert.Equal(t, "", result[2].test3, "result[2].test3 should be equal to \"\" as input is null") assert.Equal(t, 777, result[3].test, "result[3].test should be equal to 777") assert.Equal(t, 456, result[3].test2, "result[3].test2 should be equal to 456") assert.Equal(t, "string", result[3].test3, "result[3].test3 should be equal to \"string\"") assert.Equal(t, 777, result[4].test, "result[4].test should be equal to 777") assert.Equal(t, 456, result[4].test2, "result[4].test2 should be equal to 456") assert.Equal(t, "string", result[4].test3, "result[4].test3 should be equal to \"string\"") assert.Equal(t, 777, result[5].test, "result[5].test should be equal to 777") assert.Equal(t, 456, result[5].test2, "result[5].test2 should be equal to 456") assert.Equal(t, "string", result[5].test3, "result[5].test3 should be equal to \"string\"") }, }, { name: "Stream test starting with null values", streamReader: &StreamReader{ readChan: make(chan string), done: make(chan struct{}), data: ` null {"test":246,"test2":-246,"test3":"string"} {"test":247,"test2":248,"test3":"string"} `, }, expectations: func(err error, result []*TestObj, t *testing.T) { assert.Nil(t, err, "err should be nil") assert.Equal(t, 0, result[0].test, "result[0].test should be equal to 0 as input is null") assert.Equal(t, 0, result[0].test2, "result[0].test2 should be equal to 0 as input is null") assert.Equal(t, "", result[0].test3, "result[0].test3 should be equal to \"\" as input is null") assert.Equal(t, 246, result[1].test, "v[1].test should be equal to 246") assert.Equal(t, -246, result[1].test2, "v[1].test2 should be equal to -247") assert.Equal(t, "string", result[1].test3, "v[1].test3 should be equal to \"string\"") assert.Equal(t, 247, result[2].test, "result[2].test should be equal to 246") assert.Equal(t, 248, result[2].test2, "result[2].test2 should be equal to 248") assert.Equal(t, "string", result[2].test3, "result[2].test3 should be equal to \"string\"") }, }, { name: "Stream test invalid JSON", streamReader: &StreamReader{ readChan: make(chan string), done: make(chan struct{}), data: ` invalid json {"test":246,"test2":-246,"test3":"string"} {"test":247,"test2":248,"test3":"string"} `, }, expectations: func(err error, result []*TestObj, t *testing.T) { assert.NotNil(t, err, "err is not nil as JSON is invalid") assert.IsType(t, InvalidJSONError(""), err, "err is of type InvalidJSONError") assert.Equal(t, "Invalid JSON, wrong char 'i' found at position 6", err.Error(), "err message is Invalid JSON") }, }, } for _, testCase := range tests { testCase := testCase t.Run(testCase.name, func(t *testing.T) { t.Parallel() runStreamTestCaseObjects(t, testCase) }) } } func runStreamTestCaseObjects(t *testing.T, testCase StreamTestObject) { // create our channel which will receive our objects testChan := ChannelStreamObjects(make(chan *TestObj)) dec := Stream.NewDecoder(testCase.streamReader) // start decoding (will block the goroutine until something is written to the ReadWriter) go dec.DecodeStream(&testChan) // start writing to the ReadWriter go testCase.streamReader.Write() // prepare our result result := []*TestObj{} loop: for { select { case v := <-testChan: result = append(result, v) case <-dec.Done(): break loop } } testCase.expectations(dec.Err(), result, t) } type ChannelStreamObjects chan *TestObj func (c *ChannelStreamObjects) UnmarshalStream(dec *StreamDecoder) error { obj := &TestObj{} if err := dec.AddObject(obj); err != nil { return err } *c <- obj return nil } // Strings type StreamTestString struct { name string streamReader *StreamReader expectations func(error, []*string, *testing.T) } func TestStreamDecodingStringsParallel(t *testing.T) { var tests = []StreamTestString{ { name: "Stream strings basic", streamReader: &StreamReader{ readChan: make(chan string), done: make(chan struct{}), data: ` "hello" "world" "!" `, }, expectations: func(err error, result []*string, t *testing.T) { assert.Nil(t, err, "err should be nil") assert.Equal(t, "hello", *result[0], "v[0] should be equal to 'hello'") assert.Equal(t, "world", *result[1], "v[1] should be equal to 'world'") assert.Equal(t, "!", *result[2], "v[2] should be equal to '!'") }, }, { name: "Stream strings with null", streamReader: &StreamReader{ readChan: make(chan string), done: make(chan struct{}), data: ` "hello" null "!" `, }, expectations: func(err error, result []*string, t *testing.T) { assert.Nil(t, err, "err should be nil") assert.Equal(t, "hello", *result[0], "v[0] should be equal to 'hello'") assert.Equal(t, "", *result[1], "v[1] should be equal to ''") assert.Equal(t, "!", *result[2], "v[2] should be equal to '!'") }, }, { name: "Stream strings invalid JSON", streamReader: &StreamReader{ readChan: make(chan string), done: make(chan struct{}), data: ` "hello" world "!" `, }, expectations: func(err error, result []*string, t *testing.T) { assert.NotNil(t, err, "err should not be nil") assert.IsType(t, InvalidJSONError(""), err, "err is of type InvalidJSONError") assert.Equal(t, "Invalid JSON, wrong char 'w' found at position 6", err.Error(), "err message is Invalid JSON") }, }, } for _, testCase := range tests { testCase := testCase t.Run(testCase.name, func(t *testing.T) { t.Parallel() runStreamTestCaseStrings(t, testCase) }) } } func runStreamTestCaseStrings(t *testing.T, testCase StreamTestString) { // create our channel which will receive our objects testChan := ChannelStreamStrings(make(chan *string)) dec := Stream.NewDecoder(testCase.streamReader) // start decoding (will block the goroutine until something is written to the ReadWriter) go dec.DecodeStream(testChan) // start writing to the ReadWriter go testCase.streamReader.Write() // prepare our result result := []*string{} loop: for { select { case v := <-testChan: result = append(result, v) case <-dec.Done(): break loop } } testCase.expectations(dec.Err(), result, t) } func TestStreamDecodingErr(t *testing.T) { testChan := ChannelStreamStrings(make(chan *string)) dec := Stream.NewDecoder(&StreamReaderErr{}) // start decoding (will block the goroutine until something is written to the ReadWriter) go dec.DecodeStream(testChan) select { case <-dec.Done(): assert.NotNil(t, dec.Err(), "dec.Err() should not be nil") case <-testChan: assert.True(t, false, "should not be called") } } type ChannelStreamStrings chan *string func (c ChannelStreamStrings) UnmarshalStream(dec *StreamDecoder) error { str := "" if err := dec.AddString(&str); err != nil { return err } c <- &str return nil } // StreamReader mocks a stream reading chunks of data type StreamReader struct { writeCounter int readChan chan string done chan struct{} data string } func (r *StreamReader) Write() { l := len(r.data) t := 4 chunkSize := l / t carry := 0 lastWrite := 0 for r.writeCounter < t { time.Sleep(time.Duration(r.writeCounter*100) * time.Millisecond) currentChunkStart := (chunkSize) * r.writeCounter lastWrite = currentChunkStart + chunkSize r.readChan <- r.data[currentChunkStart:lastWrite] carry = l - lastWrite r.writeCounter++ } if carry > 0 { r.readChan <- r.data[lastWrite:] } r.done <- struct{}{} } func (r *StreamReader) Read(b []byte) (int, error) { select { case v := <-r.readChan: n := copy(b, v) return n, nil case <-r.done: return 0, io.EOF } } type StreamReaderErr struct{} func (r *StreamReaderErr) Read(b []byte) (int, error) { return 0, errors.New("Test Error") } // Deadline test func TestStreamDecodingDeadline(t *testing.T) { dec := Stream.NewDecoder(&StreamReader{}) now := time.Now() dec.SetDeadline(now) deadline, _ := dec.Deadline() assert.Equal(t, now.String(), deadline.String(), "dec.now and now should be equal") assert.Equal(t, now.String(), dec.deadline.String(), "dec.now and now should be equal") } func TestStreamDecodingDeadlineNotSet(t *testing.T) { dec := Stream.NewDecoder(&StreamReader{}) _, isSet := dec.Deadline() assert.Equal(t, false, isSet, "isSet should be false as deadline is not set") } // this test is only relevant for coverage func TestStreamDecodingValue(t *testing.T) { dec := Stream.NewDecoder(&StreamReader{}) v := dec.Value("") assert.Nil(t, v, "v should be nil") } func TestStreamDecodingErrNotSet(t *testing.T) { dec := Stream.NewDecoder(&StreamReader{}) assert.Nil(t, dec.Err(), "dec.Err should be nim") } func TestStreamDecodingPoolError(t *testing.T) { dec := Stream.BorrowDecoder(nil) dec.Release() defer func() { err := recover() assert.NotNil(t, err, "err shouldnt be nil") assert.IsType(t, InvalidUsagePooledDecoderError(""), err, "err should be of type InvalidUsagePooledEncoderError") assert.Equal(t, "Invalid usage of pooled decoder", err.(InvalidUsagePooledDecoderError).Error(), "err should be of type InvalidUsagePooledDecoderError") }() testChan := ChannelStreamStrings(make(chan *string)) _ = dec.DecodeStream(testChan) assert.True(t, false, "should not be called as it should have panicked") } golang-github-francoispqt-gojay-1.2.13/decode_string.go000066400000000000000000000142671365331665100231460ustar00rootroot00000000000000package gojay import ( "unsafe" ) // DecodeString reads the next JSON-encoded value from the decoder's input (io.Reader) and stores it in the string pointed to by v. // // See the documentation for Unmarshal for details about the conversion of JSON into a Go value. func (dec *Decoder) DecodeString(v *string) error { if dec.isPooled == 1 { panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) } return dec.decodeString(v) } func (dec *Decoder) decodeString(v *string) error { for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { switch dec.data[dec.cursor] { case ' ', '\n', '\t', '\r', ',': // is string continue case '"': dec.cursor++ start, end, err := dec.getString() if err != nil { return err } // we do minus one to remove the last quote d := dec.data[start : end-1] *v = *(*string)(unsafe.Pointer(&d)) dec.cursor = end return nil // is nil case 'n': dec.cursor++ err := dec.assertNull() if err != nil { return err } return nil default: dec.err = dec.makeInvalidUnmarshalErr(v) err := dec.skipData() if err != nil { return err } return nil } } return nil } func (dec *Decoder) decodeStringNull(v **string) error { for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { switch dec.data[dec.cursor] { case ' ', '\n', '\t', '\r', ',': // is string continue case '"': dec.cursor++ start, end, err := dec.getString() if err != nil { return err } if *v == nil { *v = new(string) } // we do minus one to remove the last quote d := dec.data[start : end-1] **v = *(*string)(unsafe.Pointer(&d)) dec.cursor = end return nil // is nil case 'n': dec.cursor++ err := dec.assertNull() if err != nil { return err } return nil default: dec.err = dec.makeInvalidUnmarshalErr(v) err := dec.skipData() if err != nil { return err } return nil } } return nil } func (dec *Decoder) parseEscapedString() error { if dec.cursor >= dec.length && !dec.read() { return dec.raiseInvalidJSONErr(dec.cursor) } switch dec.data[dec.cursor] { case '"': dec.data[dec.cursor] = '"' case '\\': dec.data[dec.cursor] = '\\' case '/': dec.data[dec.cursor] = '/' case 'b': dec.data[dec.cursor] = '\b' case 'f': dec.data[dec.cursor] = '\f' case 'n': dec.data[dec.cursor] = '\n' case 'r': dec.data[dec.cursor] = '\r' case 't': dec.data[dec.cursor] = '\t' case 'u': start := dec.cursor dec.cursor++ str, err := dec.parseUnicode() if err != nil { return err } diff := dec.cursor - start dec.data = append(append(dec.data[:start-1], str...), dec.data[dec.cursor:]...) dec.length = len(dec.data) dec.cursor += len(str) - diff - 1 return nil default: return dec.raiseInvalidJSONErr(dec.cursor) } dec.data = append(dec.data[:dec.cursor-1], dec.data[dec.cursor:]...) dec.length-- // Since we've lost a character, our dec.cursor offset is now // 1 past the escaped character which is precisely where we // want it. return nil } func (dec *Decoder) getString() (int, int, error) { // extract key var keyStart = dec.cursor // var str *Builder for dec.cursor < dec.length || dec.read() { switch dec.data[dec.cursor] { // string found case '"': dec.cursor = dec.cursor + 1 return keyStart, dec.cursor, nil // slash found case '\\': dec.cursor = dec.cursor + 1 err := dec.parseEscapedString() if err != nil { return 0, 0, err } default: dec.cursor = dec.cursor + 1 continue } } return 0, 0, dec.raiseInvalidJSONErr(dec.cursor) } func (dec *Decoder) skipEscapedString() error { start := dec.cursor for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { if dec.data[dec.cursor] != '\\' { d := dec.data[dec.cursor] dec.cursor = dec.cursor + 1 nSlash := dec.cursor - start switch d { case '"': // nSlash must be odd if nSlash&1 != 1 { return dec.raiseInvalidJSONErr(dec.cursor) } return nil case 'u': // is unicode, we skip the following characters and place the cursor one one byte backward to avoid it breaking when returning to skipString if err := dec.skipString(); err != nil { return err } dec.cursor-- return nil case 'n', 'r', 't', '/', 'f', 'b': return nil default: // nSlash must be even if nSlash&1 == 1 { return dec.raiseInvalidJSONErr(dec.cursor) } return nil } } } return dec.raiseInvalidJSONErr(dec.cursor) } func (dec *Decoder) skipString() error { for dec.cursor < dec.length || dec.read() { switch dec.data[dec.cursor] { // found the closing quote // let's return case '"': dec.cursor = dec.cursor + 1 return nil // solidus found start parsing an escaped string case '\\': dec.cursor = dec.cursor + 1 err := dec.skipEscapedString() if err != nil { return err } default: dec.cursor = dec.cursor + 1 continue } } return dec.raiseInvalidJSONErr(len(dec.data) - 1) } // Add Values functions // AddString decodes the JSON value within an object or an array to a *string. // If next key is not a JSON string nor null, InvalidUnmarshalError will be returned. func (dec *Decoder) AddString(v *string) error { return dec.String(v) } // AddStringNull decodes the JSON value within an object or an array to a *string. // If next key is not a JSON string nor null, InvalidUnmarshalError will be returned. // If a `null` is encountered, gojay does not change the value of the pointer. func (dec *Decoder) AddStringNull(v **string) error { return dec.StringNull(v) } // String decodes the JSON value within an object or an array to a *string. // If next key is not a JSON string nor null, InvalidUnmarshalError will be returned. func (dec *Decoder) String(v *string) error { err := dec.decodeString(v) if err != nil { return err } dec.called |= 1 return nil } // StringNull decodes the JSON value within an object or an array to a **string. // If next key is not a JSON string nor null, InvalidUnmarshalError will be returned. // If a `null` is encountered, gojay does not change the value of the pointer. func (dec *Decoder) StringNull(v **string) error { err := dec.decodeStringNull(v) if err != nil { return err } dec.called |= 1 return nil } golang-github-francoispqt-gojay-1.2.13/decode_string_test.go000066400000000000000000000457371365331665100242130ustar00rootroot00000000000000package gojay import ( "fmt" "strings" "sync" "testing" "github.com/stretchr/testify/assert" ) func TestDecoderString(t *testing.T) { testCases := []struct { name string json string expectedResult string err bool errType interface{} }{ { name: "basic-string", json: `"string"`, expectedResult: "string", err: false, }, { name: "string-solidus", json: `"\/"`, expectedResult: "/", err: false, }, { name: "basic-string", json: ``, expectedResult: "", err: false, }, { name: "basic-string", json: `""`, expectedResult: "", err: false, }, { name: "basic-string2", json: `"hello world!"`, expectedResult: "hello world!", err: false, }, { name: "escape-control-char", json: `"\n"`, expectedResult: "\n", err: false, }, { name: "escape-control-char", json: `"\\n"`, expectedResult: `\n`, err: false, }, { name: "escape-control-char", json: `"\t"`, expectedResult: "\t", err: false, }, { name: "escape-control-char", json: `"\\t"`, expectedResult: `\t`, err: false, }, { name: "escape-control-char", json: `"\b"`, expectedResult: "\b", err: false, }, { name: "escape-control-char", json: `"\\b"`, expectedResult: `\b`, err: false, }, { name: "escape-control-char", json: `"\f"`, expectedResult: "\f", err: false, }, { name: "escape-control-char", json: `"\\f"`, expectedResult: `\f`, err: false, }, { name: "escape-control-char", json: `"\r"`, expectedResult: "\r", err: false, }, { name: "escape-control-char", json: `"\`, expectedResult: "", err: true, }, { name: "escape-control-char-solidus", json: `"\/"`, expectedResult: "/", err: false, }, { name: "escape-control-char-solidus", json: `"/"`, expectedResult: "/", err: false, }, { name: "escape-control-char-solidus-escape-char", json: `"\\/"`, expectedResult: `\/`, err: false, }, { name: "escape-control-char", json: `"\\r"`, expectedResult: `\r`, err: false, }, { name: "utf8", json: `"𠜎 𠜱 𠹠𠱓 𠱸 ð ²– 𠳠𠳕 ð ´• ð µ¼ 𠵿"`, expectedResult: "𠜎 𠜱 𠹠𠱓 𠱸 ð ²– 𠳠𠳕 ð ´• ð µ¼ 𠵿", err: false, }, { name: "utf8-code-point", json: `"\u06fc"`, expectedResult: `Û¼`, err: false, }, { name: "utf8-code-point-escaped", json: `"\\u2070"`, expectedResult: `\u2070`, err: false, }, { name: "utf8-code-point-err", json: `"\u2Z70"`, expectedResult: ``, err: true, }, { name: "utf16-surrogate", json: `"\uD834\uDD1E"`, expectedResult: `ð„ž`, err: false, }, { name: "utf16-surrogate", json: `"\uD834\\"`, expectedResult: `�\`, err: false, }, { name: "utf16-surrogate", json: `"\uD834\uD834"`, expectedResult: "�\x00\x00\x00", err: false, }, { name: "utf16-surrogate", json: `"\uD834"`, expectedResult: `�`, err: false, }, { name: "utf16-surrogate-err", json: `"\uD834\`, expectedResult: ``, err: true, }, { name: "utf16-surrogate-err2", json: `"\uD834\uDZ1E`, expectedResult: ``, err: true, }, { name: "utf16-surrogate-err3", json: `"\uD834`, expectedResult: ``, err: true, }, { name: "utf16-surrogate-followed-by-control-char", json: `"\uD834\t"`, expectedResult: "�\t", err: false, }, { name: "utf16-surrogate-followed-by-control-char", json: `"\uD834\n"`, expectedResult: "�\n", err: false, }, { name: "utf16-surrogate-followed-by-control-char", json: `"\uD834\f"`, expectedResult: "�\f", err: false, }, { name: "utf16-surrogate-followed-by-control-char", json: `"\uD834\b"`, expectedResult: "�\b", err: false, }, { name: "utf16-surrogate-followed-by-control-char", json: `"\uD834\r"`, expectedResult: "�\r", err: false, }, { name: "utf16-surrogate-followed-by-control-char", json: `"\uD834\h"`, expectedResult: "", err: true, }, { name: "null", json: `null`, expectedResult: "", }, { name: "null-err", json: `nall`, expectedResult: "", err: true, }, { name: "escape quote err", json: `"test string \" escaped"`, expectedResult: `test string " escaped`, err: false, }, { name: "escape quote err2", json: `"test string \t escaped"`, expectedResult: "test string \t escaped", err: false, }, { name: "escape quote err2", json: `"test string \r escaped"`, expectedResult: "test string \r escaped", err: false, }, { name: "escape quote err2", json: `"test string \b escaped"`, expectedResult: "test string \b escaped", err: false, }, { name: "escape quote err", json: `"test string \n escaped"`, expectedResult: "test string \n escaped", err: false, }, { name: "escape quote err", json: `"test string \\\" escaped`, expectedResult: ``, err: true, errType: InvalidJSONError(""), }, { name: "escape quote err", json: `"test string \\\l escaped"`, expectedResult: ``, err: true, errType: InvalidJSONError(""), }, { name: "invalid-json", json: `invalid`, expectedResult: ``, err: true, errType: InvalidJSONError(""), }, { name: "string-complex", json: ` "string with spaces and \"escape\"d \"quotes\" and escaped line returns \n and escaped \\\\ escaped char"`, expectedResult: "string with spaces and \"escape\"d \"quotes\" and escaped line returns \n and escaped \\\\ escaped char", }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { str := "" dec := NewDecoder(strings.NewReader(testCase.json)) err := dec.Decode(&str) if testCase.err { assert.NotNil(t, err, "err should not be nil") if testCase.errType != nil { assert.IsType(t, testCase.errType, err, "err should of the given type") } } else { assert.Nil(t, err, "err should be nil") } assert.Equal(t, testCase.expectedResult, str, fmt.Sprintf("'%s' should be equal to expectedResult", str)) }) } } func TestDecoderStringNull(t *testing.T) { testCases := []struct { name string json string expectedResult string err bool errType interface{} resultIsNil bool }{ { name: "basic-string", json: `"string"`, expectedResult: "string", err: false, }, { name: "string-solidus", json: `"\/"`, expectedResult: "/", err: false, }, { name: "basic-string", json: ``, expectedResult: "", err: false, resultIsNil: true, }, { name: "basic-string", json: `""`, expectedResult: "", err: false, }, { name: "basic-string2", json: `"hello world!"`, expectedResult: "hello world!", err: false, }, { name: "escape-control-char", json: `"\n"`, expectedResult: "\n", err: false, }, { name: "escape-control-char", json: `"\\n"`, expectedResult: `\n`, err: false, }, { name: "escape-control-char", json: `"\t"`, expectedResult: "\t", err: false, }, { name: "escape-control-char", json: `"\\t"`, expectedResult: `\t`, err: false, }, { name: "escape-control-char", json: `"\b"`, expectedResult: "\b", err: false, }, { name: "escape-control-char", json: `"\\b"`, expectedResult: `\b`, err: false, }, { name: "escape-control-char", json: `"\f"`, expectedResult: "\f", err: false, }, { name: "escape-control-char", json: `"\\f"`, expectedResult: `\f`, err: false, }, { name: "escape-control-char", json: `"\r"`, expectedResult: "\r", err: false, }, { name: "escape-control-char", json: `"\`, expectedResult: "", err: true, }, { name: "escape-control-char-solidus", json: `"\/"`, expectedResult: "/", err: false, }, { name: "escape-control-char-solidus", json: `"/"`, expectedResult: "/", err: false, }, { name: "escape-control-char-solidus-escape-char", json: `"\\/"`, expectedResult: `\/`, err: false, }, { name: "escape-control-char", json: `"\\r"`, expectedResult: `\r`, err: false, }, { name: "utf8", json: `"𠜎 𠜱 𠹠𠱓 𠱸 ð ²– 𠳠𠳕 ð ´• ð µ¼ 𠵿"`, expectedResult: "𠜎 𠜱 𠹠𠱓 𠱸 ð ²– 𠳠𠳕 ð ´• ð µ¼ 𠵿", err: false, }, { name: "utf8-code-point", json: `"\u06fc"`, expectedResult: `Û¼`, err: false, }, { name: "utf8-code-point-escaped", json: `"\\u2070"`, expectedResult: `\u2070`, err: false, }, { name: "utf8-code-point-err", json: `"\u2Z70"`, expectedResult: ``, err: true, }, { name: "utf16-surrogate", json: `"\uD834\uDD1E"`, expectedResult: `ð„ž`, err: false, }, { name: "utf16-surrogate", json: `"\uD834\\"`, expectedResult: `�\`, err: false, }, { name: "utf16-surrogate", json: `"\uD834\uD834"`, expectedResult: "�\x00\x00\x00", err: false, }, { name: "utf16-surrogate", json: `"\uD834"`, expectedResult: `�`, err: false, }, { name: "utf16-surrogate-err", json: `"\uD834\`, expectedResult: ``, err: true, }, { name: "utf16-surrogate-err2", json: `"\uD834\uDZ1E`, expectedResult: ``, err: true, }, { name: "utf16-surrogate-err3", json: `"\uD834`, expectedResult: ``, err: true, }, { name: "utf16-surrogate-followed-by-control-char", json: `"\uD834\t"`, expectedResult: "�\t", err: false, }, { name: "utf16-surrogate-followed-by-control-char", json: `"\uD834\n"`, expectedResult: "�\n", err: false, }, { name: "utf16-surrogate-followed-by-control-char", json: `"\uD834\f"`, expectedResult: "�\f", err: false, }, { name: "utf16-surrogate-followed-by-control-char", json: `"\uD834\b"`, expectedResult: "�\b", err: false, }, { name: "utf16-surrogate-followed-by-control-char", json: `"\uD834\r"`, expectedResult: "�\r", err: false, }, { name: "utf16-surrogate-followed-by-control-char", json: `"\uD834\h"`, expectedResult: "", err: true, }, { name: "null", json: `null`, expectedResult: "", resultIsNil: true, }, { name: "null-err", json: `nall`, expectedResult: "", err: true, }, { name: "escape quote err", json: `"test string \" escaped"`, expectedResult: `test string " escaped`, err: false, }, { name: "escape quote err2", json: `"test string \t escaped"`, expectedResult: "test string \t escaped", err: false, }, { name: "escape quote err2", json: `"test string \r escaped"`, expectedResult: "test string \r escaped", err: false, }, { name: "escape quote err2", json: `"test string \b escaped"`, expectedResult: "test string \b escaped", err: false, }, { name: "escape quote err", json: `"test string \n escaped"`, expectedResult: "test string \n escaped", err: false, }, { name: "escape quote err", json: `"test string \\\" escaped`, expectedResult: ``, err: true, errType: InvalidJSONError(""), }, { name: "escape quote err", json: `"test string \\\l escaped"`, expectedResult: ``, err: true, errType: InvalidJSONError(""), }, { name: "invalid-json", json: `invalid`, expectedResult: ``, err: true, errType: InvalidJSONError(""), }, { name: "string-complex", json: ` "string with spaces and \"escape\"d \"quotes\" and escaped line returns \n and escaped \\\\ escaped char"`, expectedResult: "string with spaces and \"escape\"d \"quotes\" and escaped line returns \n and escaped \\\\ escaped char", }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { str := (*string)(nil) err := Unmarshal([]byte(testCase.json), &str) if testCase.err { assert.NotNil(t, err, "err should not be nil") if testCase.errType != nil { assert.IsType(t, testCase.errType, err, "err should of the given type") } return } assert.Nil(t, err, "Err must be nil") if testCase.resultIsNil { assert.Nil(t, str) } else { assert.Equal(t, testCase.expectedResult, *str, fmt.Sprintf("v must be equal to %s", testCase.expectedResult)) } }) } t.Run("decoder-api-invalid-json2", func(t *testing.T) { var v = new(string) var dec = NewDecoder(strings.NewReader(`a`)) err := dec.StringNull(&v) assert.NotNil(t, err, "Err must not be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") }) } func TestDecoderStringInvalidType(t *testing.T) { json := []byte(`1`) var v string err := Unmarshal(json, &v) assert.NotNil(t, err, "Err must not be nil as JSON is invalid") assert.IsType(t, InvalidUnmarshalError(""), err, "err message must be 'Invalid JSON'") } func TestDecoderStringDecoderAPI(t *testing.T) { var v string dec := NewDecoder(strings.NewReader(`"hello world!"`)) defer dec.Release() err := dec.DecodeString(&v) assert.Nil(t, err, "Err must be nil") assert.Equal(t, "hello world!", v, "v must be equal to 'hello world!'") } func TestDecoderStringPoolError(t *testing.T) { // reset the pool to make sure it's not full decPool = sync.Pool{ New: func() interface{} { return NewDecoder(nil) }, } result := "" dec := NewDecoder(nil) dec.Release() defer func() { err := recover() assert.NotNil(t, err, "err shouldnt be nil") assert.IsType(t, InvalidUsagePooledDecoderError(""), err, "err should be of type InvalidUsagePooledDecoderError") }() _ = dec.DecodeString(&result) assert.True(t, false, "should not be called as decoder should have panicked") } func TestDecoderSkipEscapedStringError(t *testing.T) { dec := NewDecoder(strings.NewReader(``)) defer dec.Release() err := dec.skipEscapedString() assert.NotNil(t, err, "Err must be nil") assert.IsType(t, InvalidJSONError(""), err, "err must be of type InvalidJSONError") } func TestDecoderSkipEscapedStringError2(t *testing.T) { dec := NewDecoder(strings.NewReader(`\"`)) defer dec.Release() err := dec.skipEscapedString() assert.NotNil(t, err, "Err must be nil") assert.IsType(t, InvalidJSONError(""), err, "err must be of type InvalidJSONError") } func TestDecoderSkipEscapedStringError3(t *testing.T) { dec := NewDecoder(strings.NewReader(`invalid`)) defer dec.Release() err := dec.skipEscapedString() assert.NotNil(t, err, "Err must be nil") assert.IsType(t, InvalidJSONError(""), err, "err must be of type InvalidJSONError") } func TestDecoderSkipEscapedStringError4(t *testing.T) { dec := NewDecoder(strings.NewReader(`\u12`)) defer dec.Release() err := dec.skipEscapedString() assert.NotNil(t, err, "Err must be nil") assert.IsType(t, InvalidJSONError(""), err, "err must be of type InvalidJSONError") } func TestDecoderSkipStringError(t *testing.T) { dec := NewDecoder(strings.NewReader(`invalid`)) defer dec.Release() err := dec.skipString() assert.NotNil(t, err, "Err must be nil") assert.IsType(t, InvalidJSONError(""), err, "err must be of type InvalidJSONError") } func TestSkipString(t *testing.T) { testCases := []struct { name string json string expectedResult string err bool errType interface{} }{ { name: "escape quote err", json: `test string \\" escaped"`, expectedResult: ``, err: true, errType: InvalidJSONError(""), }, { name: "escape quote err", json: `test string \\\l escaped"`, expectedResult: ``, err: true, errType: InvalidJSONError(""), }, { name: "string-solidus", json: `Asia\/Bangkok","enable":true}"`, expectedResult: "", err: false, }, { name: "string-unicode", json: `[2]\u66fe\u5b97\u5357"`, expectedResult: "", err: false, }, } for _, testCase := range testCases { dec := NewDecoder(strings.NewReader(testCase.json)) err := dec.skipString() if testCase.err { assert.NotNil(t, err, "err should not be nil") if testCase.errType != nil { assert.IsType(t, testCase.errType, err, "err should be of expected type") } return } assert.Nil(t, err, "err should be nil") } } golang-github-francoispqt-gojay-1.2.13/decode_string_unicode.go000066400000000000000000000040621365331665100246440ustar00rootroot00000000000000package gojay import ( "unicode/utf16" "unicode/utf8" ) func (dec *Decoder) getUnicode() (rune, error) { i := 0 r := rune(0) for ; (dec.cursor < dec.length || dec.read()) && i < 4; dec.cursor++ { c := dec.data[dec.cursor] if c >= '0' && c <= '9' { r = r*16 + rune(c-'0') } else if c >= 'a' && c <= 'f' { r = r*16 + rune(c-'a'+10) } else if c >= 'A' && c <= 'F' { r = r*16 + rune(c-'A'+10) } else { return 0, InvalidJSONError("Invalid unicode code point") } i++ } return r, nil } func (dec *Decoder) appendEscapeChar(str []byte, c byte) ([]byte, error) { switch c { case 't': str = append(str, '\t') case 'n': str = append(str, '\n') case 'r': str = append(str, '\r') case 'b': str = append(str, '\b') case 'f': str = append(str, '\f') case '\\': str = append(str, '\\') default: return nil, InvalidJSONError("Invalid JSON") } return str, nil } func (dec *Decoder) parseUnicode() ([]byte, error) { // get unicode after u r, err := dec.getUnicode() if err != nil { return nil, err } // no error start making new string str := make([]byte, 16, 16) i := 0 // check if code can be a surrogate utf16 if utf16.IsSurrogate(r) { if dec.cursor >= dec.length && !dec.read() { return nil, dec.raiseInvalidJSONErr(dec.cursor) } c := dec.data[dec.cursor] if c != '\\' { i += utf8.EncodeRune(str, r) return str[:i], nil } dec.cursor++ if dec.cursor >= dec.length && !dec.read() { return nil, dec.raiseInvalidJSONErr(dec.cursor) } c = dec.data[dec.cursor] if c != 'u' { i += utf8.EncodeRune(str, r) str, err = dec.appendEscapeChar(str[:i], c) if err != nil { dec.err = err return nil, err } i++ dec.cursor++ return str[:i], nil } dec.cursor++ r2, err := dec.getUnicode() if err != nil { return nil, err } combined := utf16.DecodeRune(r, r2) if combined == '\uFFFD' { i += utf8.EncodeRune(str, r) i += utf8.EncodeRune(str, r2) } else { i += utf8.EncodeRune(str, combined) } return str[:i], nil } i += utf8.EncodeRune(str, r) return str[:i], nil } golang-github-francoispqt-gojay-1.2.13/decode_test.go000066400000000000000000000352221365331665100226110ustar00rootroot00000000000000package gojay import ( "bytes" "fmt" "reflect" "testing" "github.com/stretchr/testify/assert" ) type testDecodeObj struct { test string } func (t *testDecodeObj) UnmarshalJSONObject(dec *Decoder, key string) error { switch key { case "test": return dec.AddString(&t.test) } return nil } func (t *testDecodeObj) NKeys() int { return 1 } type testDecodeSlice []*testDecodeObj func (t *testDecodeSlice) UnmarshalJSONArray(dec *Decoder) error { obj := &testDecodeObj{} if err := dec.AddObject(obj); err != nil { return err } *t = append(*t, obj) return nil } type allTypeDecodeTestCase struct { name string v interface{} d []byte expectations func(err error, v interface{}, t *testing.T) } func allTypesTestCases() []allTypeDecodeTestCase { return []allTypeDecodeTestCase{ { v: new(string), d: []byte(`"test string"`), name: "test decode string", expectations: func(err error, v interface{}, t *testing.T) { vt := v.(*string) assert.Nil(t, err, "err must be nil") assert.Equal(t, "test string", *vt, "v must be equal to 1") }, }, { v: new(*string), d: []byte(`"test string"`), name: "test decode string", expectations: func(err error, v interface{}, t *testing.T) { vt := v.(**string) assert.Nil(t, err, "err must be nil") assert.Equal(t, "test string", **vt, "v must be equal to 1") }, }, { v: new(string), d: []byte(`null`), name: "test decode string null", expectations: func(err error, v interface{}, t *testing.T) { vt := v.(*string) assert.Nil(t, err, "err must be nil") assert.Equal(t, "", *vt, "v must be equal to 1") }, }, { v: new(*string), d: []byte(`null`), name: "test decode string null", expectations: func(err error, v interface{}, t *testing.T) { vt := v.(**string) assert.Nil(t, err, "err must be nil") assert.Nil(t, *vt, "v must be nil") }, }, { v: new(*string), d: []byte(`1`), name: "test decode string null", expectations: func(err error, v interface{}, t *testing.T) { vt := v.(**string) assert.NotNil(t, err, "err must be nil") assert.Nil(t, *vt, "v must be nil") }, }, { v: new(int), d: []byte(`1`), name: "test decode int", expectations: func(err error, v interface{}, t *testing.T) { vt := v.(*int) assert.Nil(t, err, "err must be nil") assert.Equal(t, 1, *vt, "v must be equal to 1") }, }, { v: new(*int), d: []byte(`1`), name: "test decode int", expectations: func(err error, v interface{}, t *testing.T) { vt := v.(**int) assert.Nil(t, err, "err must be nil") assert.Equal(t, 1, **vt, "v must be equal to 1") }, }, { v: new(*int), d: []byte(`""`), name: "test decode int", expectations: func(err error, v interface{}, t *testing.T) { assert.NotNil(t, err, "err must be nil") }, }, { v: new(*int8), d: []byte(`1`), name: "test decode int", expectations: func(err error, v interface{}, t *testing.T) { vt := v.(**int8) assert.Nil(t, err, "err must be nil") assert.Equal(t, int8(1), **vt, "v must be equal to 1") }, }, { v: new(*int8), d: []byte(`""`), name: "test decode int", expectations: func(err error, v interface{}, t *testing.T) { assert.NotNil(t, err, "err must be nil") }, }, { v: new(*int16), d: []byte(`1`), name: "test decode int", expectations: func(err error, v interface{}, t *testing.T) { vt := v.(**int16) assert.Nil(t, err, "err must be nil") assert.Equal(t, int16(1), **vt, "v must be equal to 1") }, }, { v: new(*int16), d: []byte(`""`), name: "test decode int", expectations: func(err error, v interface{}, t *testing.T) { assert.NotNil(t, err, "err must be nil") }, }, { v: new(int64), d: []byte(`1`), name: "test decode int64", expectations: func(err error, v interface{}, t *testing.T) { vt := v.(*int64) assert.Nil(t, err, "err must be nil") assert.Equal(t, int64(1), *vt, "v must be equal to 1") }, }, { v: new(*int64), d: []byte(`1`), name: "test decode int64", expectations: func(err error, v interface{}, t *testing.T) { vt := v.(**int64) assert.Nil(t, err, "err must be nil") assert.Equal(t, int64(1), **vt, "v must be equal to 1") }, }, { v: new(*int64), d: []byte(`""`), name: "test decode int64", expectations: func(err error, v interface{}, t *testing.T) { assert.NotNil(t, err, "err must be nil") }, }, { v: new(uint64), d: []byte(`1`), name: "test decode uint64", expectations: func(err error, v interface{}, t *testing.T) { vt := v.(*uint64) assert.Nil(t, err, "err must be nil") assert.Equal(t, uint64(1), *vt, "v must be equal to 1") }, }, { v: new(*uint64), d: []byte(`1`), name: "test decode uint64", expectations: func(err error, v interface{}, t *testing.T) { vt := v.(**uint64) assert.Nil(t, err, "err must be nil") assert.Equal(t, uint64(1), **vt, "v must be equal to 1") }, }, { v: new(interface{}), d: []byte(`[{"test":"test"},{"test":"test2"}]`), name: "test decode interface", expectations: func(err error, v interface{}, t *testing.T) { assert.Nil(t, err, "err must be nil") // v is a pointer to an interface{}, we need to extract the content vCont := reflect.ValueOf(v).Elem().Interface() vt := vCont.([]interface{}) assert.Len(t, vt, 2, "len of vt must be 2") vt1 := vt[0].(map[string]interface{}) assert.Equal(t, "test", vt1["test"], "vt1['test'] must be equal to 'test'") vt2 := vt[1].(map[string]interface{}) assert.Equal(t, "test2", vt2["test"], "vt2['test'] must be equal to 'test2'") }, }, { v: new(uint64), d: []byte(`-1`), name: "test decode uint64 negative", expectations: func(err error, v interface{}, t *testing.T) { vt := v.(*uint64) assert.NotNil(t, err, "err must not be nil") assert.Equal(t, uint64(0), *vt, "v must be equal to 1") }, }, { v: new(int32), d: []byte(`1`), name: "test decode int32", expectations: func(err error, v interface{}, t *testing.T) { vt := v.(*int32) assert.Nil(t, err, "err must be nil") assert.Equal(t, int32(1), *vt, "v must be equal to 1") }, }, { v: new(*int32), d: []byte(`1`), name: "test decode int32", expectations: func(err error, v interface{}, t *testing.T) { vt := v.(**int32) assert.Nil(t, err, "err must be nil") assert.Equal(t, int32(1), **vt, "v must be equal to 1") }, }, { v: new(uint32), d: []byte(`1`), name: "test decode uint32", expectations: func(err error, v interface{}, t *testing.T) { vt := v.(*uint32) assert.Nil(t, err, "err must be nil") assert.Equal(t, uint32(1), *vt, "v must be equal to 1") }, }, { v: new(*uint32), d: []byte(`1`), name: "test decode uint32", expectations: func(err error, v interface{}, t *testing.T) { vt := v.(**uint32) assert.Nil(t, err, "err must be nil") assert.Equal(t, uint32(1), **vt, "v must be equal to 1") }, }, { v: new(uint32), d: []byte(`-1`), name: "test decode uint32 negative", expectations: func(err error, v interface{}, t *testing.T) { vt := v.(*uint32) assert.NotNil(t, err, "err must not be nil") assert.Equal(t, uint32(0), *vt, "v must be equal to 1") }, }, { v: new(*uint16), d: []byte(`1`), name: "test decode uint16", expectations: func(err error, v interface{}, t *testing.T) { vt := v.(**uint16) assert.Nil(t, err, "err must be nil") assert.Equal(t, uint16(1), **vt, "v must be equal to 1") }, }, { v: new(*uint8), d: []byte(`1`), name: "test decode uint8", expectations: func(err error, v interface{}, t *testing.T) { vt := v.(**uint8) assert.Nil(t, err, "err must be nil") assert.Equal(t, uint8(1), **vt, "v must be equal to 1") }, }, { v: new(float64), d: []byte(`1.15`), name: "test decode float64", expectations: func(err error, v interface{}, t *testing.T) { vt := v.(*float64) assert.Nil(t, err, "err must be nil") assert.Equal(t, float64(1.15), *vt, "v must be equal to 1") }, }, { v: new(*float64), d: []byte(`1.15`), name: "test decode float64", expectations: func(err error, v interface{}, t *testing.T) { vt := v.(**float64) assert.Nil(t, err, "err must be nil") assert.Equal(t, float64(1.15), **vt, "v must be equal to 1") }, }, { v: new(float64), d: []byte(`null`), name: "test decode float64 null", expectations: func(err error, v interface{}, t *testing.T) { vt := v.(*float64) assert.Nil(t, err, "err must be nil") assert.Equal(t, float64(0), *vt, "v must be equal to 1") }, }, { v: new(*float32), d: []byte(`1.15`), name: "test decode float64 null", expectations: func(err error, v interface{}, t *testing.T) { vt := v.(**float32) assert.Nil(t, err, "err must be nil") assert.Equal(t, float32(1.15), **vt, "v must be equal to 1") }, }, { v: new(bool), d: []byte(`true`), name: "test decode bool true", expectations: func(err error, v interface{}, t *testing.T) { vt := v.(*bool) assert.Nil(t, err, "err must be nil") assert.Equal(t, true, *vt, "v must be equal to 1") }, }, { v: new(*bool), d: []byte(`true`), name: "test decode bool true", expectations: func(err error, v interface{}, t *testing.T) { vt := v.(**bool) assert.Nil(t, err, "err must be nil") assert.Equal(t, true, **vt, "v must be equal to 1") }, }, { v: new(bool), d: []byte(`false`), name: "test decode bool false", expectations: func(err error, v interface{}, t *testing.T) { vt := v.(*bool) assert.Nil(t, err, "err must be nil") assert.Equal(t, false, *vt, "v must be equal to 1") }, }, { v: new(bool), d: []byte(`null`), name: "test decode bool null", expectations: func(err error, v interface{}, t *testing.T) { vt := v.(*bool) assert.Nil(t, err, "err must be nil") assert.Equal(t, false, *vt, "v must be equal to 1") }, }, { v: new(testDecodeObj), d: []byte(`{"test":"test"}`), name: "test decode object", expectations: func(err error, v interface{}, t *testing.T) { vt := v.(*testDecodeObj) assert.Nil(t, err, "err must be nil") assert.Equal(t, "test", vt.test, "v.test must be equal to 'test'") }, }, { v: new(testDecodeObj), d: []byte(`{"test":null}`), name: "test decode object null key", expectations: func(err error, v interface{}, t *testing.T) { vt := v.(*testDecodeObj) assert.Nil(t, err, "err must be nil") assert.Equal(t, "", vt.test, "v.test must be equal to 'test'") }, }, { v: new(testDecodeObj), d: []byte(`null`), name: "test decode object null", expectations: func(err error, v interface{}, t *testing.T) { vt := v.(*testDecodeObj) assert.Nil(t, err, "err must be nil") assert.Equal(t, "", vt.test, "v.test must be equal to 'test'") }, }, { v: new(testDecodeSlice), d: []byte(`[{"test":"test"}]`), name: "test decode slice", expectations: func(err error, v interface{}, t *testing.T) { vtPtr := v.(*testDecodeSlice) vt := *vtPtr assert.Nil(t, err, "err must be nil") assert.Len(t, vt, 1, "len of vt must be 1") assert.Equal(t, "test", vt[0].test, "vt[0].test must be equal to 'test'") }, }, { v: new(testDecodeSlice), d: []byte(`[{"test":"test"},{"test":"test2"}]`), name: "test decode slice", expectations: func(err error, v interface{}, t *testing.T) { vtPtr := v.(*testDecodeSlice) vt := *vtPtr assert.Nil(t, err, "err must be nil") assert.Len(t, vt, 2, "len of vt must be 2") assert.Equal(t, "test", vt[0].test, "vt[0].test must be equal to 'test'") assert.Equal(t, "test2", vt[1].test, "vt[1].test must be equal to 'test2'") }, }, { v: new(struct{}), d: []byte(`{"test":"test"}`), name: "test decode invalid type", expectations: func(err error, v interface{}, t *testing.T) { assert.NotNil(t, err, "err must not be nil") assert.IsType(t, InvalidUnmarshalError(""), err, "err must be of type InvalidUnmarshalError") assert.Equal(t, fmt.Sprintf(invalidUnmarshalErrorMsg, v), err.Error(), "err message should be equal to invalidUnmarshalErrorMsg") }, }, } } // Unmarshal tests func TestUnmarshalAllTypes(t *testing.T) { for _, testCase := range allTypesTestCases() { testCase := testCase t.Run(testCase.name, func(*testing.T) { err := Unmarshal(testCase.d, testCase.v) testCase.expectations(err, testCase.v, t) }) } } // Decode tests func TestDecodeAllTypes(t *testing.T) { for _, testCase := range allTypesTestCases() { testCase := testCase t.Run(testCase.name, func(*testing.T) { dec := NewDecoder(bytes.NewReader(testCase.d)) err := dec.Decode(testCase.v) testCase.expectations(err, testCase.v, t) }) } } func TestUnmarshalJSONObjects(t *testing.T) { testCases := []struct { name string v UnmarshalerJSONObject d []byte expectations func(err error, v interface{}, t *testing.T) }{ { v: new(testDecodeObj), d: []byte(`{"test":"test"}`), name: "test decode object", expectations: func(err error, v interface{}, t *testing.T) { vt := v.(*testDecodeObj) assert.Nil(t, err, "err must be nil") assert.Equal(t, "test", vt.test, "v.test must be equal to 'test'") }, }, { v: new(testDecodeObj), d: []byte(`{"test":null}`), name: "test decode object null key", expectations: func(err error, v interface{}, t *testing.T) { vt := v.(*testDecodeObj) assert.Nil(t, err, "err must be nil") assert.Equal(t, "", vt.test, "v.test must be equal to 'test'") }, }, { v: new(testDecodeObj), d: []byte(`null`), name: "test decode object null", expectations: func(err error, v interface{}, t *testing.T) { vt := v.(*testDecodeObj) assert.Nil(t, err, "err must be nil") assert.Equal(t, "", vt.test, "v.test must be equal to 'test'") }, }, { v: new(testDecodeObj), d: []byte(`invalid json`), name: "test decode object null", expectations: func(err error, v interface{}, t *testing.T) { assert.NotNil(t, err, "err must not be nil") assert.IsType(t, InvalidJSONError(""), err, "err must be of type InvalidJSONError") }, }, } for _, testCase := range testCases { testCase := testCase t.Run(testCase.name, func(*testing.T) { err := UnmarshalJSONObject(testCase.d, testCase.v) testCase.expectations(err, testCase.v, t) }) } } golang-github-francoispqt-gojay-1.2.13/decode_time.go000066400000000000000000000023161365331665100225660ustar00rootroot00000000000000package gojay import ( "time" ) // DecodeTime decodes time with the given format func (dec *Decoder) DecodeTime(v *time.Time, format string) error { if dec.isPooled == 1 { panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) } return dec.decodeTime(v, format) } func (dec *Decoder) decodeTime(v *time.Time, format string) error { if format == time.RFC3339 { var ej = make(EmbeddedJSON, 0, 20) if err := dec.decodeEmbeddedJSON(&ej); err != nil { return err } if err := v.UnmarshalJSON(ej); err != nil { return err } return nil } var str string if err := dec.decodeString(&str); err != nil { return err } tt, err := time.Parse(format, str) if err != nil { return err } *v = tt return nil } // Add Values functions // AddTime decodes the JSON value within an object or an array to a *time.Time with the given format func (dec *Decoder) AddTime(v *time.Time, format string) error { return dec.Time(v, format) } // Time decodes the JSON value within an object or an array to a *time.Time with the given format func (dec *Decoder) Time(v *time.Time, format string) error { err := dec.decodeTime(v, format) if err != nil { return err } dec.called |= 1 return nil } golang-github-francoispqt-gojay-1.2.13/decode_time_test.go000066400000000000000000000062071365331665100236300ustar00rootroot00000000000000package gojay import ( "strings" "sync" "testing" "time" "github.com/stretchr/testify/assert" ) func TestDecodeTime(t *testing.T) { testCases := []struct { name string json string format string err bool expectedTime string }{ { name: "basic", json: `"2018-02-18"`, format: `2006-01-02`, err: false, expectedTime: "2018-02-18", }, { name: "basic", json: `"2017-01-02T15:04:05Z"`, format: time.RFC3339, err: false, expectedTime: "2017-01-02T15:04:05Z", }, { name: "basic", json: `"2017-01-02T15:04:05ZINVALID"`, format: time.RFC3339, err: true, expectedTime: "", }, { name: "basic", json: `"2017-01-02T15:04:05ZINVALID`, format: time.RFC1123, err: true, expectedTime: "", }, { name: "basic", json: `"2017-01-02T15:04:05ZINVALID"`, format: time.RFC1123, err: true, expectedTime: "", }, { name: "basic", json: `"2017-01-02T15:04:05ZINVALID`, format: time.RFC3339, err: true, expectedTime: "", }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { tm := time.Time{} dec := NewDecoder(strings.NewReader(testCase.json)) err := dec.DecodeTime(&tm, testCase.format) if !testCase.err { assert.Nil(t, err) assert.Equal(t, testCase.expectedTime, tm.Format(testCase.format)) return } assert.NotNil(t, err) }) } } func TestDecodeAddTime(t *testing.T) { testCases := []struct { name string json string format string err bool expectedTime string }{ { name: "basic", json: `"2018-02-18"`, format: `2006-01-02`, err: false, expectedTime: "2018-02-18", }, { name: "basic", json: ` "2017-01-02T15:04:05Z"`, format: time.RFC3339, err: false, expectedTime: "2017-01-02T15:04:05Z", }, { name: "basic", json: ` "2017-01-02T15:04:05ZINVALID"`, format: time.RFC3339, err: true, expectedTime: "", }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { tm := time.Time{} dec := NewDecoder(strings.NewReader(testCase.json)) err := dec.AddTime(&tm, testCase.format) if !testCase.err { assert.Nil(t, err) assert.Equal(t, testCase.expectedTime, tm.Format(testCase.format)) return } assert.NotNil(t, err) }) } } func TestDecoderTimePoolError(t *testing.T) { // reset the pool to make sure it's not full decPool = sync.Pool{ New: func() interface{} { return NewDecoder(nil) }, } dec := NewDecoder(nil) dec.Release() defer func() { err := recover() assert.NotNil(t, err, "err shouldnt be nil") assert.IsType(t, InvalidUsagePooledDecoderError(""), err, "err should be of type InvalidUsagePooledDecoderError") }() _ = dec.DecodeTime(&time.Time{}, time.RFC3339) assert.True(t, false, "should not be called as decoder should have panicked") } golang-github-francoispqt-gojay-1.2.13/decode_unsafe.go000066400000000000000000000055321365331665100231140ustar00rootroot00000000000000package gojay import ( "fmt" ) // Unsafe is the structure holding the unsafe version of the API. // The difference between unsafe api and regular api is that the regular API // copies the buffer passed to Unmarshal functions to a new internal buffer. // Making it safer because internally GoJay uses unsafe.Pointer to transform slice of bytes into a string. var Unsafe = decUnsafe{} type decUnsafe struct{} func (u decUnsafe) UnmarshalJSONArray(data []byte, v UnmarshalerJSONArray) error { dec := borrowDecoder(nil, 0) defer dec.Release() dec.data = data dec.length = len(data) _, err := dec.decodeArray(v) return err } func (u decUnsafe) UnmarshalJSONObject(data []byte, v UnmarshalerJSONObject) error { dec := borrowDecoder(nil, 0) defer dec.Release() dec.data = data dec.length = len(data) _, err := dec.decodeObject(v) return err } func (u decUnsafe) Unmarshal(data []byte, v interface{}) error { var err error var dec *Decoder switch vt := v.(type) { case *string: dec = borrowDecoder(nil, 0) dec.length = len(data) dec.data = data err = dec.decodeString(vt) case *int: dec = borrowDecoder(nil, 0) dec.length = len(data) dec.data = data err = dec.decodeInt(vt) case *int8: dec = borrowDecoder(nil, 0) dec.length = len(data) dec.data = data err = dec.decodeInt8(vt) case *int16: dec = borrowDecoder(nil, 0) dec.length = len(data) dec.data = data err = dec.decodeInt16(vt) case *int32: dec = borrowDecoder(nil, 0) dec.length = len(data) dec.data = data err = dec.decodeInt32(vt) case *int64: dec = borrowDecoder(nil, 0) dec.length = len(data) dec.data = data err = dec.decodeInt64(vt) case *uint8: dec = borrowDecoder(nil, 0) dec.length = len(data) dec.data = data err = dec.decodeUint8(vt) case *uint16: dec = borrowDecoder(nil, 0) dec.length = len(data) dec.data = data err = dec.decodeUint16(vt) case *uint32: dec = borrowDecoder(nil, 0) dec.length = len(data) dec.data = data err = dec.decodeUint32(vt) case *uint64: dec = borrowDecoder(nil, 0) dec.length = len(data) dec.data = data err = dec.decodeUint64(vt) case *float64: dec = borrowDecoder(nil, 0) dec.length = len(data) dec.data = data err = dec.decodeFloat64(vt) case *float32: dec = borrowDecoder(nil, 0) dec.length = len(data) dec.data = data err = dec.decodeFloat32(vt) case *bool: dec = borrowDecoder(nil, 0) dec.length = len(data) dec.data = data err = dec.decodeBool(vt) case UnmarshalerJSONObject: dec = borrowDecoder(nil, 0) dec.length = len(data) dec.data = data _, err = dec.decodeObject(vt) case UnmarshalerJSONArray: dec = borrowDecoder(nil, 0) dec.length = len(data) dec.data = data _, err = dec.decodeArray(vt) default: return InvalidUnmarshalError(fmt.Sprintf(invalidUnmarshalErrorMsg, vt)) } defer dec.Release() if err != nil { return err } return dec.err } golang-github-francoispqt-gojay-1.2.13/decode_unsafe_test.go000066400000000000000000000273751365331665100241640ustar00rootroot00000000000000package gojay import ( "fmt" "testing" "github.com/stretchr/testify/assert" ) func TestUnmarshalUnsafeAllTypes(t *testing.T) { testCases := []struct { name string v interface{} d []byte expectations func(err error, v interface{}, t *testing.T) }{ { v: new(string), d: []byte(`"test string"`), name: "test decode string", expectations: func(err error, v interface{}, t *testing.T) { vt := v.(*string) assert.Nil(t, err, "err must be nil") assert.Equal(t, "test string", *vt, "v must be equal to 1") }, }, { v: new(string), d: []byte(`null`), name: "test decode string null", expectations: func(err error, v interface{}, t *testing.T) { vt := v.(*string) assert.Nil(t, err, "err must be nil") assert.Equal(t, "", *vt, "v must be equal to 1") }, }, { v: new(int), d: []byte(`1`), name: "test decode int", expectations: func(err error, v interface{}, t *testing.T) { vt := v.(*int) assert.Nil(t, err, "err must be nil") assert.Equal(t, 1, *vt, "v must be equal to 1") }, }, { v: new(int8), d: []byte(`1`), name: "test decode int8", expectations: func(err error, v interface{}, t *testing.T) { vt := v.(*int8) assert.Nil(t, err, "err must be nil") assert.Equal(t, int8(1), *vt, "v must be equal to 1") }, }, { v: new(int16), d: []byte(`1`), name: "test decode int16", expectations: func(err error, v interface{}, t *testing.T) { vt := v.(*int16) assert.Nil(t, err, "err must be nil") assert.Equal(t, int16(1), *vt, "v must be equal to 1") }, }, { v: new(int32), d: []byte(`1`), name: "test decode int32", expectations: func(err error, v interface{}, t *testing.T) { vt := v.(*int32) assert.Nil(t, err, "err must be nil") assert.Equal(t, int32(1), *vt, "v must be equal to 1") }, }, { v: new(int64), d: []byte(`1`), name: "test decode int64", expectations: func(err error, v interface{}, t *testing.T) { vt := v.(*int64) assert.Nil(t, err, "err must be nil") assert.Equal(t, int64(1), *vt, "v must be equal to 1") }, }, { v: new(uint64), d: []byte(`1`), name: "test decode uint64", expectations: func(err error, v interface{}, t *testing.T) { vt := v.(*uint64) assert.Nil(t, err, "err must be nil") assert.Equal(t, uint64(1), *vt, "v must be equal to 1") }, }, { v: new(uint64), d: []byte(`-1`), name: "test decode uint64 negative", expectations: func(err error, v interface{}, t *testing.T) { vt := v.(*uint64) assert.NotNil(t, err, "err must not be nil") assert.Equal(t, uint64(0), *vt, "v must be equal to 1") }, }, { v: new(int32), d: []byte(`1`), name: "test decode int32", expectations: func(err error, v interface{}, t *testing.T) { vt := v.(*int32) assert.Nil(t, err, "err must be nil") assert.Equal(t, int32(1), *vt, "v must be equal to 1") }, }, { v: new(uint32), d: []byte(`1`), name: "test decode uint32", expectations: func(err error, v interface{}, t *testing.T) { vt := v.(*uint32) assert.Nil(t, err, "err must be nil") assert.Equal(t, uint32(1), *vt, "v must be equal to 1") }, }, { v: new(uint32), d: []byte(`-1`), name: "test decode uint32 negative", expectations: func(err error, v interface{}, t *testing.T) { vt := v.(*uint32) assert.NotNil(t, err, "err must not be nil") assert.Equal(t, uint32(0), *vt, "v must be equal to 1") }, }, { v: new(uint8), d: []byte(`1`), name: "test decode int8", expectations: func(err error, v interface{}, t *testing.T) { vt := v.(*uint8) assert.Nil(t, err, "err must be nil") assert.Equal(t, uint8(1), *vt, "v must be equal to 1") }, }, { v: new(uint16), d: []byte(`1`), name: "test decode uint16", expectations: func(err error, v interface{}, t *testing.T) { vt := v.(*uint16) assert.Nil(t, err, "err must be nil") assert.Equal(t, uint16(1), *vt, "v must be equal to 1") }, }, { v: new(float64), d: []byte(`1.15`), name: "test decode float64", expectations: func(err error, v interface{}, t *testing.T) { vt := v.(*float64) assert.Nil(t, err, "err must be nil") assert.Equal(t, float64(1.15), *vt, "v must be equal to 1") }, }, { v: new(float64), d: []byte(`null`), name: "test decode float64 null", expectations: func(err error, v interface{}, t *testing.T) { vt := v.(*float64) assert.Nil(t, err, "err must be nil") assert.Equal(t, float64(0), *vt, "v must be equal to 1") }, }, { v: new(float32), d: []byte(`1.15`), name: "test decode float64", expectations: func(err error, v interface{}, t *testing.T) { vt := v.(*float32) assert.Nil(t, err, "err must be nil") assert.Equal(t, float32(1.15), *vt, "v must be equal to 1") }, }, { v: new(bool), d: []byte(`true`), name: "test decode bool true", expectations: func(err error, v interface{}, t *testing.T) { vt := v.(*bool) assert.Nil(t, err, "err must be nil") assert.Equal(t, true, *vt, "v must be equal to 1") }, }, { v: new(bool), d: []byte(`false`), name: "test decode bool false", expectations: func(err error, v interface{}, t *testing.T) { vt := v.(*bool) assert.Nil(t, err, "err must be nil") assert.Equal(t, false, *vt, "v must be equal to 1") }, }, { v: new(bool), d: []byte(`null`), name: "test decode bool null", expectations: func(err error, v interface{}, t *testing.T) { vt := v.(*bool) assert.Nil(t, err, "err must be nil") assert.Equal(t, false, *vt, "v must be equal to 1") }, }, { v: new(testDecodeObj), d: []byte(`{"test":"test"}`), name: "test decode object", expectations: func(err error, v interface{}, t *testing.T) { vt := v.(*testDecodeObj) assert.Nil(t, err, "err must be nil") assert.Equal(t, "test", vt.test, "v.test must be equal to 'test'") }, }, { v: new(testDecodeObj), d: []byte(`{"test":null}`), name: "test decode object null key", expectations: func(err error, v interface{}, t *testing.T) { vt := v.(*testDecodeObj) assert.Nil(t, err, "err must be nil") assert.Equal(t, "", vt.test, "v.test must be equal to 'test'") }, }, { v: new(testDecodeObj), d: []byte(`null`), name: "test decode object null", expectations: func(err error, v interface{}, t *testing.T) { vt := v.(*testDecodeObj) assert.Nil(t, err, "err must be nil") assert.Equal(t, "", vt.test, "v.test must be equal to 'test'") }, }, { v: new(testDecodeSlice), d: []byte(`[{"test":"test"}]`), name: "test decode slice", expectations: func(err error, v interface{}, t *testing.T) { vtPtr := v.(*testDecodeSlice) vt := *vtPtr assert.Nil(t, err, "err must be nil") assert.Len(t, vt, 1, "len of vt must be 1") assert.Equal(t, "test", vt[0].test, "vt[0].test must be equal to 'test'") }, }, { v: new(testDecodeSlice), d: []byte(`[{"test":"test"},{"test":"test2"}]`), name: "test decode slice", expectations: func(err error, v interface{}, t *testing.T) { vtPtr := v.(*testDecodeSlice) vt := *vtPtr assert.Nil(t, err, "err must be nil") assert.Len(t, vt, 2, "len of vt must be 2") assert.Equal(t, "test", vt[0].test, "vt[0].test must be equal to 'test'") assert.Equal(t, "test2", vt[1].test, "vt[1].test must be equal to 'test2'") }, }, { v: new(struct{}), d: []byte(`{"test":"test"}`), name: "test decode invalid type", expectations: func(err error, v interface{}, t *testing.T) { assert.NotNil(t, err, "err must not be nil") assert.IsType(t, InvalidUnmarshalError(""), err, "err must be of type InvalidUnmarshalError") assert.Equal(t, fmt.Sprintf(invalidUnmarshalErrorMsg, v), err.Error(), "err message should be equal to invalidUnmarshalErrorMsg") }, }, { v: new(int), d: []byte(`1a2`), name: "test decode invalid json", expectations: func(err error, v interface{}, t *testing.T) { assert.NotNil(t, err, "err must not be nil") assert.IsType(t, InvalidJSONError(""), err, "err must be of type InvalidJSONError") }, }, } for _, testCase := range testCases { testCase := testCase t.Run(testCase.name, func(*testing.T) { err := Unsafe.Unmarshal(testCase.d, testCase.v) testCase.expectations(err, testCase.v, t) }) } } func TestUnmarshalUnsafeObjects(t *testing.T) { testCases := []struct { name string v UnmarshalerJSONObject d []byte expectations func(err error, v interface{}, t *testing.T) }{ { v: new(testDecodeObj), d: []byte(`{"test":"test"}`), name: "test decode object", expectations: func(err error, v interface{}, t *testing.T) { vt := v.(*testDecodeObj) assert.Nil(t, err, "err must be nil") assert.Equal(t, "test", vt.test, "v.test must be equal to 'test'") }, }, { v: new(testDecodeObj), d: []byte(`{"test":null}`), name: "test decode object null key", expectations: func(err error, v interface{}, t *testing.T) { vt := v.(*testDecodeObj) assert.Nil(t, err, "err must be nil") assert.Equal(t, "", vt.test, "v.test must be equal to 'test'") }, }, { v: new(testDecodeObj), d: []byte(`null`), name: "test decode object null", expectations: func(err error, v interface{}, t *testing.T) { vt := v.(*testDecodeObj) assert.Nil(t, err, "err must be nil") assert.Equal(t, "", vt.test, "v.test must be equal to 'test'") }, }, { v: new(testDecodeObj), d: []byte(`invalid json`), name: "test decode object null", expectations: func(err error, v interface{}, t *testing.T) { assert.NotNil(t, err, "err must not be nil") assert.IsType(t, InvalidJSONError(""), err, "err must be of type InvalidJSONError") }, }, } for _, testCase := range testCases { testCase := testCase t.Run(testCase.name, func(*testing.T) { err := Unsafe.UnmarshalJSONObject(testCase.d, testCase.v) testCase.expectations(err, testCase.v, t) }) } } func TestUnmarshalUnsafeArrays(t *testing.T) { testCases := []struct { name string v UnmarshalerJSONArray d []byte expectations func(err error, v interface{}, t *testing.T) }{ { v: new(testDecodeSlice), d: []byte(`[{"test":"test"}]`), name: "test decode slice", expectations: func(err error, v interface{}, t *testing.T) { vtPtr := v.(*testDecodeSlice) vt := *vtPtr assert.Nil(t, err, "err must be nil") assert.Len(t, vt, 1, "len of vt must be 1") assert.Equal(t, "test", vt[0].test, "vt[0].test must be equal to 'test'") }, }, { v: new(testDecodeSlice), d: []byte(`[{"test":"test"},{"test":"test2"}]`), name: "test decode slice", expectations: func(err error, v interface{}, t *testing.T) { vtPtr := v.(*testDecodeSlice) vt := *vtPtr assert.Nil(t, err, "err must be nil") assert.Len(t, vt, 2, "len of vt must be 2") assert.Equal(t, "test", vt[0].test, "vt[0].test must be equal to 'test'") assert.Equal(t, "test2", vt[1].test, "vt[1].test must be equal to 'test2'") }, }, { v: new(testDecodeSlice), d: []byte(`invalid json`), name: "test decode object null", expectations: func(err error, v interface{}, t *testing.T) { assert.NotNil(t, err, "err must not be nil") assert.IsType(t, InvalidJSONError(""), err, "err must be of type InvalidJSONError") }, }, } for _, testCase := range testCases { testCase := testCase t.Run(testCase.name, func(*testing.T) { err := Unsafe.UnmarshalJSONArray(testCase.d, testCase.v) testCase.expectations(err, testCase.v, t) }) } } golang-github-francoispqt-gojay-1.2.13/encode.go000066400000000000000000000110731365331665100215620ustar00rootroot00000000000000package gojay import ( "encoding/json" "fmt" "io" ) var nullBytes = []byte("null") // MarshalJSONArray returns the JSON encoding of v, an implementation of MarshalerJSONArray. // // // Example: // type TestSlice []*TestStruct // // func (t TestSlice) MarshalJSONArray(enc *Encoder) { // for _, e := range t { // enc.AddObject(e) // } // } // // func main() { // test := &TestSlice{ // &TestStruct{123456}, // &TestStruct{7890}, // } // b, _ := Marshal(test) // fmt.Println(b) // [{"id":123456},{"id":7890}] // } func MarshalJSONArray(v MarshalerJSONArray) ([]byte, error) { enc := BorrowEncoder(nil) enc.grow(512) enc.writeByte('[') v.(MarshalerJSONArray).MarshalJSONArray(enc) enc.writeByte(']') defer func() { enc.buf = make([]byte, 0, 512) enc.Release() }() return enc.buf, nil } // MarshalJSONObject returns the JSON encoding of v, an implementation of MarshalerJSONObject. // // Example: // type Object struct { // id int // } // func (s *Object) MarshalJSONObject(enc *gojay.Encoder) { // enc.IntKey("id", s.id) // } // func (s *Object) IsNil() bool { // return s == nil // } // // func main() { // test := &Object{ // id: 123456, // } // b, _ := gojay.Marshal(test) // fmt.Println(b) // {"id":123456} // } func MarshalJSONObject(v MarshalerJSONObject) ([]byte, error) { enc := BorrowEncoder(nil) enc.grow(512) defer func() { enc.buf = make([]byte, 0, 512) enc.Release() }() return enc.encodeObject(v) } // Marshal returns the JSON encoding of v. // // If v is nil, not an implementation MarshalerJSONObject or MarshalerJSONArray or not one of the following types: // string, int, int8, int16, int32, int64, uint8, uint16, uint32, uint64, float64, float32, bool // Marshal returns an InvalidMarshalError. func Marshal(v interface{}) ([]byte, error) { return marshal(v, false) } // MarshalAny returns the JSON encoding of v. // // If v is nil, not an implementation MarshalerJSONObject or MarshalerJSONArray or not one of the following types: // string, int, int8, int16, int32, int64, uint8, uint16, uint32, uint64, float64, float32, bool // MarshalAny falls back to "json/encoding" package to marshal the value. func MarshalAny(v interface{}) ([]byte, error) { return marshal(v, true) } func marshal(v interface{}, any bool) ([]byte, error) { var ( enc = BorrowEncoder(nil) buf []byte err error ) defer func() { enc.buf = make([]byte, 0, 512) enc.Release() }() buf, err = func() ([]byte, error) { switch vt := v.(type) { case MarshalerJSONObject: return enc.encodeObject(vt) case MarshalerJSONArray: return enc.encodeArray(vt) case string: return enc.encodeString(vt) case bool: return enc.encodeBool(vt) case int: return enc.encodeInt(vt) case int64: return enc.encodeInt64(vt) case int32: return enc.encodeInt(int(vt)) case int16: return enc.encodeInt(int(vt)) case int8: return enc.encodeInt(int(vt)) case uint64: return enc.encodeInt(int(vt)) case uint32: return enc.encodeInt(int(vt)) case uint16: return enc.encodeInt(int(vt)) case uint8: return enc.encodeInt(int(vt)) case float64: return enc.encodeFloat(vt) case float32: return enc.encodeFloat32(vt) case *EmbeddedJSON: return enc.encodeEmbeddedJSON(vt) default: if any { return json.Marshal(vt) } return nil, InvalidMarshalError(fmt.Sprintf(invalidMarshalErrorMsg, vt)) } }() return buf, err } // MarshalerJSONObject is the interface to implement for struct to be encoded type MarshalerJSONObject interface { MarshalJSONObject(enc *Encoder) IsNil() bool } // MarshalerJSONArray is the interface to implement // for a slice or an array to be encoded type MarshalerJSONArray interface { MarshalJSONArray(enc *Encoder) IsNil() bool } // An Encoder writes JSON values to an output stream. type Encoder struct { buf []byte isPooled byte w io.Writer err error hasKeys bool keys []string } // AppendBytes allows a modular usage by appending bytes manually to the current state of the buffer. func (enc *Encoder) AppendBytes(b []byte) { enc.writeBytes(b) } // AppendByte allows a modular usage by appending a single byte manually to the current state of the buffer. func (enc *Encoder) AppendByte(b byte) { enc.writeByte(b) } // Buf returns the Encoder's buffer. func (enc *Encoder) Buf() []byte { return enc.buf } // Write writes to the io.Writer and resets the buffer. func (enc *Encoder) Write() (int, error) { i, err := enc.w.Write(enc.buf) enc.buf = enc.buf[:0] return i, err } func (enc *Encoder) getPreviousRune() byte { last := len(enc.buf) - 1 return enc.buf[last] } golang-github-francoispqt-gojay-1.2.13/encode_array.go000066400000000000000000000132331365331665100227600ustar00rootroot00000000000000package gojay // EncodeArray encodes an implementation of MarshalerJSONArray to JSON func (enc *Encoder) EncodeArray(v MarshalerJSONArray) error { if enc.isPooled == 1 { panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder")) } _, _ = enc.encodeArray(v) _, err := enc.Write() if err != nil { enc.err = err return err } return nil } func (enc *Encoder) encodeArray(v MarshalerJSONArray) ([]byte, error) { enc.grow(200) enc.writeByte('[') v.MarshalJSONArray(enc) enc.writeByte(']') return enc.buf, enc.err } // AddArray adds an implementation of MarshalerJSONArray to be encoded, must be used inside a slice or array encoding (does not encode a key) // value must implement Marshaler func (enc *Encoder) AddArray(v MarshalerJSONArray) { enc.Array(v) } // AddArrayOmitEmpty adds an array or slice to be encoded, must be used inside a slice or array encoding (does not encode a key) // value must implement MarshalerAddArrayOmitEmpty func (enc *Encoder) AddArrayOmitEmpty(v MarshalerJSONArray) { enc.ArrayOmitEmpty(v) } // AddArrayNullEmpty adds an array or slice to be encoded, must be used inside a slice or array encoding (does not encode a key) // value must implement Marshaler, if v is empty, `null` will be encoded` func (enc *Encoder) AddArrayNullEmpty(v MarshalerJSONArray) { enc.ArrayNullEmpty(v) } // AddArrayKey adds an array or slice to be encoded, must be used inside an object as it will encode a key // value must implement Marshaler func (enc *Encoder) AddArrayKey(key string, v MarshalerJSONArray) { enc.ArrayKey(key, v) } // AddArrayKeyOmitEmpty adds an array or slice to be encoded and skips it if it is nil. // Must be called inside an object as it will encode a key. func (enc *Encoder) AddArrayKeyOmitEmpty(key string, v MarshalerJSONArray) { enc.ArrayKeyOmitEmpty(key, v) } // AddArrayKeyNullEmpty adds an array or slice to be encoded and skips it if it is nil. // Must be called inside an object as it will encode a key. `null` will be encoded` func (enc *Encoder) AddArrayKeyNullEmpty(key string, v MarshalerJSONArray) { enc.ArrayKeyNullEmpty(key, v) } // Array adds an implementation of MarshalerJSONArray to be encoded, must be used inside a slice or array encoding (does not encode a key) // value must implement Marshaler func (enc *Encoder) Array(v MarshalerJSONArray) { if v.IsNil() { enc.grow(3) r := enc.getPreviousRune() if r != '[' { enc.writeByte(',') } enc.writeByte('[') enc.writeByte(']') return } enc.grow(100) r := enc.getPreviousRune() if r != '[' { enc.writeByte(',') } enc.writeByte('[') v.MarshalJSONArray(enc) enc.writeByte(']') } // ArrayOmitEmpty adds an array or slice to be encoded, must be used inside a slice or array encoding (does not encode a key) // value must implement Marshaler func (enc *Encoder) ArrayOmitEmpty(v MarshalerJSONArray) { if v.IsNil() { return } enc.grow(4) r := enc.getPreviousRune() if r != '[' { enc.writeByte(',') } enc.writeByte('[') v.MarshalJSONArray(enc) enc.writeByte(']') } // ArrayNullEmpty adds an array or slice to be encoded, must be used inside a slice or array encoding (does not encode a key) // value must implement Marshaler func (enc *Encoder) ArrayNullEmpty(v MarshalerJSONArray) { enc.grow(4) r := enc.getPreviousRune() if r != '[' { enc.writeByte(',') } if v.IsNil() { enc.writeBytes(nullBytes) return } enc.writeByte('[') v.MarshalJSONArray(enc) enc.writeByte(']') } // ArrayKey adds an array or slice to be encoded, must be used inside an object as it will encode a key // value must implement Marshaler func (enc *Encoder) ArrayKey(key string, v MarshalerJSONArray) { if enc.hasKeys { if !enc.keyExists(key) { return } } if v.IsNil() { enc.grow(2 + len(key)) r := enc.getPreviousRune() if r != '{' { enc.writeByte(',') } enc.writeByte('"') enc.writeStringEscape(key) enc.writeBytes(objKeyArr) enc.writeByte(']') return } enc.grow(5 + len(key)) r := enc.getPreviousRune() if r != '{' { enc.writeByte(',') } enc.writeByte('"') enc.writeStringEscape(key) enc.writeBytes(objKeyArr) v.MarshalJSONArray(enc) enc.writeByte(']') } // ArrayKeyOmitEmpty adds an array or slice to be encoded and skips if it is nil. // Must be called inside an object as it will encode a key. func (enc *Encoder) ArrayKeyOmitEmpty(key string, v MarshalerJSONArray) { if enc.hasKeys { if !enc.keyExists(key) { return } } if v.IsNil() { return } enc.grow(5 + len(key)) r := enc.getPreviousRune() if r != '{' { enc.writeByte(',') } enc.writeByte('"') enc.writeStringEscape(key) enc.writeBytes(objKeyArr) v.MarshalJSONArray(enc) enc.writeByte(']') } // ArrayKeyNullEmpty adds an array or slice to be encoded and encodes `null`` if it is nil. // Must be called inside an object as it will encode a key. func (enc *Encoder) ArrayKeyNullEmpty(key string, v MarshalerJSONArray) { if enc.hasKeys { if !enc.keyExists(key) { return } } enc.grow(5 + len(key)) r := enc.getPreviousRune() if r != '{' { enc.writeByte(',') } if v.IsNil() { enc.writeBytes(nullBytes) return } enc.writeByte('"') enc.writeStringEscape(key) enc.writeBytes(objKeyArr) v.MarshalJSONArray(enc) enc.writeByte(']') } // EncodeArrayFunc is a custom func type implementing MarshaleArray. // Use it to cast a func(*Encoder) to Marshal an object. // // enc := gojay.NewEncoder(io.Writer) // enc.EncodeArray(gojay.EncodeArrayFunc(func(enc *gojay.Encoder) { // enc.AddStringKey("hello", "world") // })) type EncodeArrayFunc func(*Encoder) // MarshalJSONArray implements MarshalerJSONArray. func (f EncodeArrayFunc) MarshalJSONArray(enc *Encoder) { f(enc) } // IsNil implements MarshalerJSONArray. func (f EncodeArrayFunc) IsNil() bool { return f == nil } golang-github-francoispqt-gojay-1.2.13/encode_array_test.go000066400000000000000000000242611365331665100240220ustar00rootroot00000000000000package gojay import ( "strings" "testing" "github.com/stretchr/testify/assert" ) type TestEncodingArrStrings []string func (t TestEncodingArrStrings) MarshalJSONArray(enc *Encoder) { for _, e := range t { enc.AddString(e) } } func (t TestEncodingArrStrings) IsNil() bool { return len(t) == 0 } type TestEncodingArr []*TestEncoding func (t TestEncodingArr) MarshalJSONArray(enc *Encoder) { for _, e := range t { enc.AddObject(e) } } func (t TestEncodingArr) IsNil() bool { return t == nil } type testEncodingArrInterfaces []interface{} func (t testEncodingArrInterfaces) MarshalJSONArray(enc *Encoder) { for _, e := range t { enc.AddInterface(e) } } func (t testEncodingArrInterfaces) IsNil() bool { return t == nil } func TestEncoderArrayMarshalAPI(t *testing.T) { t.Run("array-objects", func(t *testing.T) { v := &TestEncodingArr{ &TestEncoding{ test: "hello world", test2: "漢字", testInt: 1, testBool: true, testInterface: 1, sub: &SubObject{ test1: 10, test2: "hello world", test3: 1.23543, testBool: true, sub: &SubObject{ test1: 10, testBool: false, test2: "hello world", }, }, }, &TestEncoding{ test: "hello world", test2: "漢字", testInt: 1, testBool: true, sub: &SubObject{ test1: 10, test2: "hello world", test3: 1.23543, testBool: true, sub: &SubObject{ test1: 10, testBool: false, test2: "hello world", }, }, }, nil, } r, err := Marshal(v) assert.Nil(t, err, "Error should be nil") assert.Equal( t, `[{"test":"hello world","test2":"漢字","testInt":1,"testBool":true,`+ `"testArr":[],"testF64":0,"testF32":0,"testInterface":1,"sub":{"test1":10,"test2":"hello world",`+ `"test3":1.23543,"testBool":true,"sub":{"test1":10,"test2":"hello world",`+ `"test3":0,"testBool":false,"sub":{}}}},{"test":"hello world","test2":"漢字","testInt":1,`+ `"testBool":true,"testArr":[],"testF64":0,"testF32":0,"sub":{"test1":10,"test2":"hello world","test3":1.23543,`+ `"testBool":true,"sub":{"test1":10,"test2":"hello world","test3":0,"testBool":false,"sub":{}}}},{}]`, string(r), "Result of marshalling is different as the one expected") }) t.Run("array-interfaces", func(t *testing.T) { v := &testEncodingArrInterfaces{ 1, int64(1), int32(1), int16(1), int8(1), uint64(1), uint32(1), uint16(1), uint8(1), float64(1.31), float32(1.31), &TestEncodingArr{}, &TestEncodingArrStrings{}, true, false, "test", &TestEncoding{ test: "hello world", test2: "foobar", testInt: 1, testBool: true, }, } r, err := MarshalJSONArray(v) assert.Nil(t, err, "Error should be nil") assert.Equal( t, `[1,1,1,1,1,1,1,1,1.31,1.31,[],[],true,false,"test",{"test":"hello world","test2":"foobar","testInt":1,"testBool":true,"testArr":[],"testF64":0,"testF32":0,"sub":{}}]`, string(r), "Result of marshalling is different as the one expected") }) } func TestEncoderArrayEncodeAPI(t *testing.T) { t.Run("array-interfaces", func(t *testing.T) { v := &testEncodingArrInterfaces{ 1, int64(1), int32(1), int16(1), int8(1), uint64(1), uint32(1), uint16(1), uint8(1), float64(1.31), // float32(1.31), &TestEncodingArr{}, true, "test", &TestEncoding{ test: "hello world", test2: "foobar", testInt: 1, testBool: true, }, } builder := &strings.Builder{} enc := BorrowEncoder(builder) defer enc.Release() err := enc.EncodeArray(v) assert.Nil(t, err, "Error should be nil") assert.Equal( t, `[1,1,1,1,1,1,1,1,1.31,[],true,"test",{"test":"hello world","test2":"foobar","testInt":1,"testBool":true,"testArr":[],"testF64":0,"testF32":0,"sub":{}}]`, builder.String(), "Result of marshalling is different as the one expected") }) t.Run("array-interfaces-write-error", func(t *testing.T) { v := &testEncodingArrInterfaces{} w := TestWriterError("") enc := BorrowEncoder(w) defer enc.Release() err := enc.EncodeArray(v) assert.NotNil(t, err, "err should not be nil") }) } // Array add with omit key tests type TestEncodingIntOmitEmpty []int func (t TestEncodingIntOmitEmpty) MarshalJSONArray(enc *Encoder) { for _, e := range t { enc.AddIntOmitEmpty(e) } } func (t TestEncodingIntOmitEmpty) IsNil() bool { return t == nil } type TestEncodingStringOmitEmpty []string func (t TestEncodingStringOmitEmpty) MarshalJSONArray(enc *Encoder) { for _, e := range t { enc.AddStringOmitEmpty(e) } } func (t TestEncodingStringOmitEmpty) IsNil() bool { return t == nil } type TestEncodingFloatOmitEmpty []float64 func (t TestEncodingFloatOmitEmpty) MarshalJSONArray(enc *Encoder) { for _, e := range t { enc.AddFloatOmitEmpty(e) } } func (t TestEncodingFloatOmitEmpty) IsNil() bool { return t == nil } type TestEncodingFloat32OmitEmpty []float32 func (t TestEncodingFloat32OmitEmpty) MarshalJSONArray(enc *Encoder) { for _, e := range t { enc.AddFloat32OmitEmpty(e) } } func (t TestEncodingFloat32OmitEmpty) IsNil() bool { return t == nil } type TestEncodingBoolOmitEmpty []bool func (t TestEncodingBoolOmitEmpty) MarshalJSONArray(enc *Encoder) { for _, e := range t { enc.AddBoolOmitEmpty(e) } } func (t TestEncodingBoolOmitEmpty) IsNil() bool { return len(t) == 0 } type TestEncodingArrOmitEmpty []TestEncodingBoolOmitEmpty func (t TestEncodingArrOmitEmpty) MarshalJSONArray(enc *Encoder) { for _, e := range t { enc.AddArrayOmitEmpty(e) } } func (t TestEncodingArrOmitEmpty) IsNil() bool { return len(t) == 0 } type TestObjEmpty struct { empty bool } func (t *TestObjEmpty) MarshalJSONObject(enc *Encoder) { } func (t *TestObjEmpty) IsNil() bool { return !t.empty } type TestEncodingObjOmitEmpty []*TestObjEmpty func (t TestEncodingObjOmitEmpty) MarshalJSONArray(enc *Encoder) { for _, e := range t { enc.AddObjectOmitEmpty(e) } } func (t TestEncodingObjOmitEmpty) IsNil() bool { return t == nil } func TestEncoderArrayOmitEmpty(t *testing.T) { t.Run("omit-int", func(t *testing.T) { intArr := TestEncodingIntOmitEmpty{0, 1, 0, 1} b, err := Marshal(intArr) assert.Nil(t, err, "err must be nil") assert.Equal(t, `[1,1]`, string(b), "string(b) must be equal to `[1,1]`") }) t.Run("omit-float", func(t *testing.T) { floatArr := TestEncodingFloatOmitEmpty{0, 1, 0, 1} b, err := Marshal(floatArr) assert.Nil(t, err, "err must be nil") assert.Equal(t, `[1,1]`, string(b), "string(b) must be equal to `[1,1]`") }) t.Run("omit-float32", func(t *testing.T) { float32Arr := TestEncodingFloat32OmitEmpty{0, 1, 0, 1} b, err := Marshal(float32Arr) assert.Nil(t, err, "err must be nil") assert.Equal(t, `[1,1]`, string(b), "string(b) must be equal to `[1,1]`") }) t.Run("omit-string", func(t *testing.T) { stringArr := TestEncodingStringOmitEmpty{"", "hello", "", "world"} b, err := Marshal(stringArr) assert.Nil(t, err, "err must be nil") assert.Equal(t, `["hello","world"]`, string(b), "string(b) must be equal to `[\"hello\",\"world\"]`") }) t.Run("omit-bool", func(t *testing.T) { boolArr := TestEncodingBoolOmitEmpty{false, true, false, true} b, err := Marshal(boolArr) assert.Nil(t, err, "err must be nil") assert.Equal(t, `[true,true]`, string(b), "string(b) must be equal to `[true,true]`") }) t.Run("omit-arr", func(t *testing.T) { arrArr := TestEncodingArrOmitEmpty{TestEncodingBoolOmitEmpty{true}, nil, TestEncodingBoolOmitEmpty{true}, nil} b, err := Marshal(arrArr) assert.Nil(t, err, "err must be nil") assert.Equal(t, `[[true],[true]]`, string(b), "string(b) must be equal to `[[true],[true]]`") }) t.Run("omit-obj", func(t *testing.T) { objArr := TestEncodingObjOmitEmpty{&TestObjEmpty{true}, &TestObjEmpty{false}, &TestObjEmpty{true}, &TestObjEmpty{false}} b, err := Marshal(objArr) assert.Nil(t, err, "err must be nil") assert.Equal(t, `[{},{}]`, string(b), "string(b) must be equal to `[{},{}]`") }) } func TestEncoderArrErrors(t *testing.T) { t.Run("add-interface-error", func(t *testing.T) { builder := &strings.Builder{} enc := NewEncoder(builder) enc.AddInterface(nil) assert.Nil(t, enc.err, "enc.Err() should not be nil") assert.Equal(t, "", builder.String(), "builder.String() should not be ''") }) t.Run("array-pooled-error", func(t *testing.T) { v := &testEncodingArrInterfaces{} enc := BorrowEncoder(nil) enc.Release() defer func() { err := recover() assert.NotNil(t, err, "err shouldnt be nil") assert.IsType(t, InvalidUsagePooledEncoderError(""), err, "err should be of type InvalidUsagePooledEncoderError") assert.Equal(t, "Invalid usage of pooled encoder", err.(InvalidUsagePooledEncoderError).Error(), "err should be of type InvalidUsagePooledDecoderError") }() _ = enc.EncodeArray(v) assert.True(t, false, "should not be called as it should have panicked") }) } func TestEncoderArrayFunc(t *testing.T) { var f EncodeArrayFunc assert.True(t, f.IsNil()) } func TestEncodeArrayNullEmpty(t *testing.T) { var testCases = []struct { name, baseJSON, expectedJSON string }{ { name: "basic 1st elem", baseJSON: "[", expectedJSON: `[null,["foo"]`, }, { name: "basic 1st elem", baseJSON: `["test"`, expectedJSON: `["test",null,["foo"]`, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { var b strings.Builder var enc = NewEncoder(&b) enc.writeString(testCase.baseJSON) enc.AddArrayNullEmpty(&TestEncodingArrStrings{}) enc.ArrayNullEmpty(&TestEncodingArrStrings{"foo"}) }) } } func TestEncodeArrayKeyNullEmpty(t *testing.T) { var testCases = []struct { name, baseJSON, expectedJSON string }{ { name: "basic 1st elem", baseJSON: "{", expectedJSON: `{"foo":null,"bar":["foo"]`, }, { name: "basic 1st elem", baseJSON: `{"test":"test"`, expectedJSON: `{"test":"test","foo":null,"bar":["foo"]`, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { var b strings.Builder var enc = NewEncoder(&b) enc.writeString(testCase.baseJSON) enc.AddArrayKeyNullEmpty("foo", &TestEncodingArrStrings{}) enc.ArrayKeyNullEmpty("bar", &TestEncodingArrStrings{"foo"}) }) } } golang-github-francoispqt-gojay-1.2.13/encode_bool.go000066400000000000000000000077641365331665100226110ustar00rootroot00000000000000package gojay import "strconv" // EncodeBool encodes a bool to JSON func (enc *Encoder) EncodeBool(v bool) error { if enc.isPooled == 1 { panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder")) } _, _ = enc.encodeBool(v) _, err := enc.Write() if err != nil { enc.err = err return err } return nil } // encodeBool encodes a bool to JSON func (enc *Encoder) encodeBool(v bool) ([]byte, error) { enc.grow(5) if v { enc.writeString("true") } else { enc.writeString("false") } return enc.buf, enc.err } // AddBool adds a bool to be encoded, must be used inside a slice or array encoding (does not encode a key) func (enc *Encoder) AddBool(v bool) { enc.Bool(v) } // AddBoolOmitEmpty adds a bool to be encoded, must be used inside a slice or array encoding (does not encode a key) func (enc *Encoder) AddBoolOmitEmpty(v bool) { enc.BoolOmitEmpty(v) } // AddBoolNullEmpty adds a bool to be encoded, must be used inside a slice or array encoding (does not encode a key) func (enc *Encoder) AddBoolNullEmpty(v bool) { enc.BoolNullEmpty(v) } // AddBoolKey adds a bool to be encoded, must be used inside an object as it will encode a key. func (enc *Encoder) AddBoolKey(key string, v bool) { enc.BoolKey(key, v) } // AddBoolKeyOmitEmpty adds a bool to be encoded and skips if it is zero value. // Must be used inside an object as it will encode a key. func (enc *Encoder) AddBoolKeyOmitEmpty(key string, v bool) { enc.BoolKeyOmitEmpty(key, v) } // AddBoolKeyNullEmpty adds a bool to be encoded and encodes `null` if it is zero value. // Must be used inside an object as it will encode a key. func (enc *Encoder) AddBoolKeyNullEmpty(key string, v bool) { enc.BoolKeyNullEmpty(key, v) } // Bool adds a bool to be encoded, must be used inside a slice or array encoding (does not encode a key) func (enc *Encoder) Bool(v bool) { enc.grow(5) r := enc.getPreviousRune() if r != '[' { enc.writeByte(',') } if v { enc.writeString("true") } else { enc.writeString("false") } } // BoolOmitEmpty adds a bool to be encoded, must be used inside a slice or array encoding (does not encode a key) func (enc *Encoder) BoolOmitEmpty(v bool) { if v == false { return } enc.grow(5) r := enc.getPreviousRune() if r != '[' { enc.writeByte(',') } enc.writeString("true") } // BoolNullEmpty adds a bool to be encoded, must be used inside a slice or array encoding (does not encode a key) func (enc *Encoder) BoolNullEmpty(v bool) { enc.grow(5) r := enc.getPreviousRune() if r != '[' { enc.writeByte(',') } if v == false { enc.writeBytes(nullBytes) return } enc.writeString("true") } // BoolKey adds a bool to be encoded, must be used inside an object as it will encode a key. func (enc *Encoder) BoolKey(key string, value bool) { if enc.hasKeys { if !enc.keyExists(key) { return } } enc.grow(5 + len(key)) r := enc.getPreviousRune() if r != '{' { enc.writeByte(',') } enc.writeByte('"') enc.writeStringEscape(key) enc.writeBytes(objKey) enc.buf = strconv.AppendBool(enc.buf, value) } // BoolKeyOmitEmpty adds a bool to be encoded and skips it if it is zero value. // Must be used inside an object as it will encode a key. func (enc *Encoder) BoolKeyOmitEmpty(key string, v bool) { if enc.hasKeys { if !enc.keyExists(key) { return } } if v == false { return } enc.grow(5 + len(key)) r := enc.getPreviousRune() if r != '{' { enc.writeByte(',') } enc.writeByte('"') enc.writeStringEscape(key) enc.writeBytes(objKey) enc.buf = strconv.AppendBool(enc.buf, v) } // BoolKeyNullEmpty adds a bool to be encoded and skips it if it is zero value. // Must be used inside an object as it will encode a key. func (enc *Encoder) BoolKeyNullEmpty(key string, v bool) { if enc.hasKeys { if !enc.keyExists(key) { return } } enc.grow(5 + len(key)) r := enc.getPreviousRune() if r != '{' { enc.writeByte(',') } enc.writeByte('"') enc.writeStringEscape(key) enc.writeBytes(objKey) if v == false { enc.writeBytes(nullBytes) return } enc.buf = strconv.AppendBool(enc.buf, v) } golang-github-francoispqt-gojay-1.2.13/encode_bool_test.go000066400000000000000000000064241365331665100236400ustar00rootroot00000000000000package gojay import ( "strings" "testing" "github.com/stretchr/testify/assert" ) func TestEncoderBoolMarshalAPI(t *testing.T) { t.Run("true", func(t *testing.T) { b, err := Marshal(true) assert.Nil(t, err, "err must be nil") assert.Equal(t, "true", string(b), "string(b) must be equal to 'true'") }) t.Run("false", func(t *testing.T) { b, err := Marshal(false) assert.Nil(t, err, "err must be nil") assert.Equal(t, "false", string(b), "string(b) must be equal to 'false'") }) } func TestEncoderBoolEncodeAPI(t *testing.T) { t.Run("true", func(t *testing.T) { builder := &strings.Builder{} enc := BorrowEncoder(builder) defer enc.Release() err := enc.EncodeBool(true) assert.Nil(t, err, "err must be nil") assert.Equal(t, "true", builder.String(), "string(b) must be equal to 'true'") }) t.Run("false", func(t *testing.T) { builder := &strings.Builder{} enc := BorrowEncoder(builder) defer enc.Release() err := enc.EncodeBool(false) assert.Nil(t, err, "err must be nil") assert.Equal(t, "false", builder.String(), "string(b) must be equal to 'false'") }) } func TestEncoderBoolErrors(t *testing.T) { t.Run("pool-error", func(t *testing.T) { builder := &strings.Builder{} enc := BorrowEncoder(builder) enc.isPooled = 1 defer func() { err := recover() assert.NotNil(t, err, "err shouldnt be nil") assert.IsType(t, InvalidUsagePooledEncoderError(""), err, "err should be of type InvalidUsagePooledEncoderError") assert.Equal(t, "Invalid usage of pooled encoder", err.(InvalidUsagePooledEncoderError).Error(), "err should be of type InvalidUsagePooledEncoderError") }() _ = enc.EncodeBool(false) assert.True(t, false, "should not be called as it should have panicked") }) t.Run("encode-api-write-error", func(t *testing.T) { v := true w := TestWriterError("") enc := BorrowEncoder(w) defer enc.Release() err := enc.EncodeBool(v) assert.NotNil(t, err, "err should not be nil") }) } func TestEncoderBoolNullEmpty(t *testing.T) { var testCases = []struct { name string baseJSON string expectedJSON string }{ { name: "basic 1st elem", baseJSON: "[", expectedJSON: "[null,true", }, { name: "basic 2nd elem", baseJSON: `["test"`, expectedJSON: `["test",null,true`, }, } for _, testCase := range testCases { t.Run("true", func(t *testing.T) { var b strings.Builder var enc = NewEncoder(&b) enc.writeString(testCase.baseJSON) enc.BoolNullEmpty(false) enc.AddBoolNullEmpty(true) enc.Write() assert.Equal(t, testCase.expectedJSON, b.String()) }) } } func TestEncoderBoolNullKeyEmpty(t *testing.T) { var testCases = []struct { name string baseJSON string expectedJSON string }{ { name: "basic 1st elem", baseJSON: "{", expectedJSON: `{"foo":null,"bar":true`, }, { name: "basic 2nd elem", baseJSON: `{"test":"test"`, expectedJSON: `{"test":"test","foo":null,"bar":true`, }, } for _, testCase := range testCases { t.Run("true", func(t *testing.T) { var b strings.Builder var enc = NewEncoder(&b) enc.writeString(testCase.baseJSON) enc.BoolKeyNullEmpty("foo", false) enc.AddBoolKeyNullEmpty("bar", true) enc.Write() assert.Equal(t, testCase.expectedJSON, b.String()) }) } } golang-github-francoispqt-gojay-1.2.13/encode_builder.go000066400000000000000000000030521365331665100232660ustar00rootroot00000000000000package gojay const hex = "0123456789abcdef" // grow grows b's capacity, if necessary, to guarantee space for // another n bytes. After grow(n), at least n bytes can be written to b // without another allocation. If n is negative, grow panics. func (enc *Encoder) grow(n int) { if cap(enc.buf)-len(enc.buf) < n { Buf := make([]byte, len(enc.buf), 2*cap(enc.buf)+n) copy(Buf, enc.buf) enc.buf = Buf } } // Write appends the contents of p to b's Buffer. // Write always returns len(p), nil. func (enc *Encoder) writeBytes(p []byte) { enc.buf = append(enc.buf, p...) } func (enc *Encoder) writeTwoBytes(b1 byte, b2 byte) { enc.buf = append(enc.buf, b1, b2) } // WriteByte appends the byte c to b's Buffer. // The returned error is always nil. func (enc *Encoder) writeByte(c byte) { enc.buf = append(enc.buf, c) } // WriteString appends the contents of s to b's Buffer. // It returns the length of s and a nil error. func (enc *Encoder) writeString(s string) { enc.buf = append(enc.buf, s...) } func (enc *Encoder) writeStringEscape(s string) { l := len(s) for i := 0; i < l; i++ { c := s[i] if c >= 0x20 && c != '\\' && c != '"' { enc.writeByte(c) continue } switch c { case '\\', '"': enc.writeTwoBytes('\\', c) case '\n': enc.writeTwoBytes('\\', 'n') case '\f': enc.writeTwoBytes('\\', 'f') case '\b': enc.writeTwoBytes('\\', 'b') case '\r': enc.writeTwoBytes('\\', 'r') case '\t': enc.writeTwoBytes('\\', 't') default: enc.writeString(`\u00`) enc.writeTwoBytes(hex[c>>4], hex[c&0xF]) } continue } } golang-github-francoispqt-gojay-1.2.13/encode_builder_test.go000066400000000000000000000000161365331665100243220ustar00rootroot00000000000000package gojay golang-github-francoispqt-gojay-1.2.13/encode_embedded_json.go000066400000000000000000000045741365331665100244340ustar00rootroot00000000000000package gojay // EncodeEmbeddedJSON encodes an embedded JSON. // is basically sets the internal buf as the value pointed by v and calls the io.Writer.Write() func (enc *Encoder) EncodeEmbeddedJSON(v *EmbeddedJSON) error { if enc.isPooled == 1 { panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder")) } enc.buf = *v _, err := enc.Write() if err != nil { return err } return nil } func (enc *Encoder) encodeEmbeddedJSON(v *EmbeddedJSON) ([]byte, error) { enc.writeBytes(*v) return enc.buf, nil } // AddEmbeddedJSON adds an EmbeddedJSON to be encoded. // // It basically blindly writes the bytes to the final buffer. Therefore, // it expects the JSON to be of proper format. func (enc *Encoder) AddEmbeddedJSON(v *EmbeddedJSON) { enc.grow(len(*v) + 4) r := enc.getPreviousRune() if r != '[' { enc.writeByte(',') } enc.writeBytes(*v) } // AddEmbeddedJSONOmitEmpty adds an EmbeddedJSON to be encoded or skips it if nil pointer or empty. // // It basically blindly writes the bytes to the final buffer. Therefore, // it expects the JSON to be of proper format. func (enc *Encoder) AddEmbeddedJSONOmitEmpty(v *EmbeddedJSON) { if v == nil || len(*v) == 0 { return } r := enc.getPreviousRune() if r != '[' { enc.writeByte(',') } enc.writeBytes(*v) } // AddEmbeddedJSONKey adds an EmbeddedJSON and a key to be encoded. // // It basically blindly writes the bytes to the final buffer. Therefore, // it expects the JSON to be of proper format. func (enc *Encoder) AddEmbeddedJSONKey(key string, v *EmbeddedJSON) { if enc.hasKeys { if !enc.keyExists(key) { return } } enc.grow(len(key) + len(*v) + 5) r := enc.getPreviousRune() if r != '{' { enc.writeByte(',') } enc.writeByte('"') enc.writeStringEscape(key) enc.writeBytes(objKey) enc.writeBytes(*v) } // AddEmbeddedJSONKeyOmitEmpty adds an EmbeddedJSON and a key to be encoded or skips it if nil pointer or empty. // // It basically blindly writes the bytes to the final buffer. Therefore, // it expects the JSON to be of proper format. func (enc *Encoder) AddEmbeddedJSONKeyOmitEmpty(key string, v *EmbeddedJSON) { if enc.hasKeys { if !enc.keyExists(key) { return } } if v == nil || len(*v) == 0 { return } enc.grow(len(key) + len(*v) + 5) r := enc.getPreviousRune() if r != '{' { enc.writeByte(',') } enc.writeByte('"') enc.writeStringEscape(key) enc.writeBytes(objKey) enc.writeBytes(*v) } golang-github-francoispqt-gojay-1.2.13/encode_embedded_json_test.go000066400000000000000000000073761365331665100254760ustar00rootroot00000000000000package gojay import ( "strings" "testing" "github.com/stretchr/testify/assert" ) func (r *Request) MarshalJSONObject(enc *Encoder) { enc.AddStringKey("id", r.id) enc.AddStringKey("method", r.method) enc.AddEmbeddedJSONKey("params", &r.params) params2 := EmbeddedJSON([]byte(``)) enc.AddEmbeddedJSONKeyOmitEmpty("params2", ¶ms2) params3 := EmbeddedJSON([]byte(`"test"`)) enc.AddEmbeddedJSONKeyOmitEmpty("params3", ¶ms3) enc.AddIntKey("more", r.more) } func (r *Request) IsNil() bool { return r == nil } type EmbeddedJSONArr []EmbeddedJSON func (ear EmbeddedJSONArr) MarshalJSONArray(enc *Encoder) { for _, e := range ear { enc.AddEmbeddedJSON(&e) } } func (ear EmbeddedJSONArr) IsNil() bool { return len(ear) == 0 } type EmbeddedJSONOmitEmptyArr []EmbeddedJSON func (ear EmbeddedJSONOmitEmptyArr) MarshalJSONArray(enc *Encoder) { for _, e := range ear { enc.AddEmbeddedJSONOmitEmpty(&e) } } func (ear EmbeddedJSONOmitEmptyArr) IsNil() bool { return len(ear) == 0 } func TestEncodingEmbeddedJSON(t *testing.T) { t.Run("basic-embedded-json", func(t *testing.T) { ej := EmbeddedJSON([]byte(`"test"`)) b := &strings.Builder{} enc := BorrowEncoder(b) err := enc.Encode(&ej) assert.Nil(t, err, "err should be nil") assert.Equal(t, b.String(), `"test"`, "b should be equal to content of EmbeddedJSON") }) t.Run("basic-embedded-json-marshal-api", func(t *testing.T) { ej := EmbeddedJSON([]byte(`"test"`)) b, err := Marshal(&ej) assert.Nil(t, err, "err should be nil") assert.Equal(t, string(b), `"test"`, "b should be equal to content of EmbeddedJSON") }) t.Run("object-embedded-json", func(t *testing.T) { req := Request{ id: "test", method: "GET", params: EmbeddedJSON([]byte(`"test"`)), } b := &strings.Builder{} enc := BorrowEncoder(b) err := enc.EncodeObject(&req) assert.Nil(t, err, "err should be nil") assert.Equal( t, b.String(), `{"id":"test","method":"GET","params":"test","params3":"test","more":0}`, "b should be equal to content of EmbeddedJSON", ) }) t.Run("array-embedded-json", func(t *testing.T) { ear := EmbeddedJSONArr{ []byte(`"test"`), []byte(`{"test":"test"}`), } b := &strings.Builder{} enc := BorrowEncoder(b) err := enc.EncodeArray(ear) assert.Nil(t, err, "err should be nil") assert.Equal( t, b.String(), `["test",{"test":"test"}]`, "b should be equal to content of EmbeddedJSON", ) }) t.Run("array-embedded-json-omit-empty", func(t *testing.T) { ear := EmbeddedJSONOmitEmptyArr{ []byte(`"test"`), []byte(``), []byte(`{"test":"test"}`), []byte(``), []byte(`{"test":"test"}`), } b := &strings.Builder{} enc := BorrowEncoder(b) err := enc.EncodeArray(ear) assert.Nil(t, err, "err should be nil") assert.Equal( t, b.String(), `["test",{"test":"test"},{"test":"test"}]`, "b should be equal to content of EmbeddedJSON", ) }) t.Run("write-error", func(t *testing.T) { w := TestWriterError("") v := EmbeddedJSON([]byte(`"test"`)) enc := NewEncoder(w) err := enc.EncodeEmbeddedJSON(&v) assert.NotNil(t, err, "Error should not be nil") assert.Equal(t, "Test Error", err.Error(), "err.Error() should be 'Test Error'") }) t.Run("pool-error", func(t *testing.T) { v := EmbeddedJSON([]byte(`"test"`)) enc := BorrowEncoder(nil) enc.isPooled = 1 defer func() { err := recover() assert.NotNil(t, err, "err shouldnt be nil") assert.IsType(t, InvalidUsagePooledEncoderError(""), err, "err should be of type InvalidUsagePooledEncoderError") assert.Equal(t, "Invalid usage of pooled encoder", err.(InvalidUsagePooledEncoderError).Error(), "err should be of type InvalidUsagePooledDecoderError") }() _ = enc.EncodeEmbeddedJSON(&v) assert.True(t, false, "should not be called as it should have panicked") }) } golang-github-francoispqt-gojay-1.2.13/encode_example_test.go000066400000000000000000000017131365331665100243340ustar00rootroot00000000000000package gojay_test import ( "fmt" "log" "os" "github.com/francoispqt/gojay" ) func ExampleMarshal_string() { str := "gojay" d, err := gojay.Marshal(str) if err != nil { log.Fatal(err) } fmt.Println(string(d)) // "gojay" } func ExampleMarshal_bool() { b := true d, err := gojay.Marshal(b) if err != nil { log.Fatal(err) } fmt.Println(string(d)) // true } func ExampleNewEncoder() { enc := gojay.BorrowEncoder(os.Stdout) var str = "gojay" err := enc.EncodeString(str) if err != nil { log.Fatal(err) } // Output: // "gojay" } func ExampleBorrowEncoder() { enc := gojay.BorrowEncoder(os.Stdout) defer enc.Release() var str = "gojay" err := enc.EncodeString(str) if err != nil { log.Fatal(err) } // Output: // "gojay" } func ExampleEncoder_EncodeString() { enc := gojay.BorrowEncoder(os.Stdout) defer enc.Release() var str = "gojay" err := enc.EncodeString(str) if err != nil { log.Fatal(err) } // Output: // "gojay" } golang-github-francoispqt-gojay-1.2.13/encode_interface.go000066400000000000000000000077711365331665100236140ustar00rootroot00000000000000package gojay import ( "fmt" ) // Encode encodes a value to JSON. // // If Encode cannot find a way to encode the type to JSON // it will return an InvalidMarshalError. func (enc *Encoder) Encode(v interface{}) error { if enc.isPooled == 1 { panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder")) } switch vt := v.(type) { case string: return enc.EncodeString(vt) case bool: return enc.EncodeBool(vt) case MarshalerJSONArray: return enc.EncodeArray(vt) case MarshalerJSONObject: return enc.EncodeObject(vt) case int: return enc.EncodeInt(vt) case int64: return enc.EncodeInt64(vt) case int32: return enc.EncodeInt(int(vt)) case int8: return enc.EncodeInt(int(vt)) case uint64: return enc.EncodeUint64(vt) case uint32: return enc.EncodeInt(int(vt)) case uint16: return enc.EncodeInt(int(vt)) case uint8: return enc.EncodeInt(int(vt)) case float64: return enc.EncodeFloat(vt) case float32: return enc.EncodeFloat32(vt) case *EmbeddedJSON: return enc.EncodeEmbeddedJSON(vt) default: return InvalidMarshalError(fmt.Sprintf(invalidMarshalErrorMsg, vt)) } } // AddInterface adds an interface{} to be encoded, must be used inside a slice or array encoding (does not encode a key) func (enc *Encoder) AddInterface(value interface{}) { switch vt := value.(type) { case string: enc.AddString(vt) case bool: enc.AddBool(vt) case MarshalerJSONArray: enc.AddArray(vt) case MarshalerJSONObject: enc.AddObject(vt) case int: enc.AddInt(vt) case int64: enc.AddInt(int(vt)) case int32: enc.AddInt(int(vt)) case int8: enc.AddInt(int(vt)) case uint64: enc.AddUint64(vt) case uint32: enc.AddInt(int(vt)) case uint16: enc.AddInt(int(vt)) case uint8: enc.AddInt(int(vt)) case float64: enc.AddFloat(vt) case float32: enc.AddFloat32(vt) default: if vt != nil { enc.err = InvalidMarshalError(fmt.Sprintf(invalidMarshalErrorMsg, vt)) return } return } } // AddInterfaceKey adds an interface{} to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) AddInterfaceKey(key string, value interface{}) { switch vt := value.(type) { case string: enc.AddStringKey(key, vt) case bool: enc.AddBoolKey(key, vt) case MarshalerJSONArray: enc.AddArrayKey(key, vt) case MarshalerJSONObject: enc.AddObjectKey(key, vt) case int: enc.AddIntKey(key, vt) case int64: enc.AddIntKey(key, int(vt)) case int32: enc.AddIntKey(key, int(vt)) case int16: enc.AddIntKey(key, int(vt)) case int8: enc.AddIntKey(key, int(vt)) case uint64: enc.AddIntKey(key, int(vt)) case uint32: enc.AddIntKey(key, int(vt)) case uint16: enc.AddIntKey(key, int(vt)) case uint8: enc.AddIntKey(key, int(vt)) case float64: enc.AddFloatKey(key, vt) case float32: enc.AddFloat32Key(key, vt) default: if vt != nil { enc.err = InvalidMarshalError(fmt.Sprintf(invalidMarshalErrorMsg, vt)) return } return } } // AddInterfaceKeyOmitEmpty adds an interface{} to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) AddInterfaceKeyOmitEmpty(key string, v interface{}) { switch vt := v.(type) { case string: enc.AddStringKeyOmitEmpty(key, vt) case bool: enc.AddBoolKeyOmitEmpty(key, vt) case MarshalerJSONArray: enc.AddArrayKeyOmitEmpty(key, vt) case MarshalerJSONObject: enc.AddObjectKeyOmitEmpty(key, vt) case int: enc.AddIntKeyOmitEmpty(key, vt) case int64: enc.AddIntKeyOmitEmpty(key, int(vt)) case int32: enc.AddIntKeyOmitEmpty(key, int(vt)) case int16: enc.AddIntKeyOmitEmpty(key, int(vt)) case int8: enc.AddIntKeyOmitEmpty(key, int(vt)) case uint64: enc.AddIntKeyOmitEmpty(key, int(vt)) case uint32: enc.AddIntKeyOmitEmpty(key, int(vt)) case uint16: enc.AddIntKeyOmitEmpty(key, int(vt)) case uint8: enc.AddIntKeyOmitEmpty(key, int(vt)) case float64: enc.AddFloatKeyOmitEmpty(key, vt) case float32: enc.AddFloat32KeyOmitEmpty(key, vt) default: if vt != nil { enc.err = InvalidMarshalError(fmt.Sprintf(invalidMarshalErrorMsg, vt)) return } return } } golang-github-francoispqt-gojay-1.2.13/encode_interface_test.go000066400000000000000000000121761365331665100246460ustar00rootroot00000000000000package gojay import ( "fmt" "strings" "testing" "github.com/stretchr/testify/assert" ) var encoderTestCases = []struct { v interface{} expectations func(t *testing.T, b string, err error) }{ { v: 100, expectations: func(t *testing.T, b string, err error) { assert.Nil(t, err, "err should be nil") assert.Equal(t, "100", b, "b should equal 100") }, }, { v: int64(100), expectations: func(t *testing.T, b string, err error) { assert.Nil(t, err, "err should be nil") assert.Equal(t, "100", b, "b should equal 100") }, }, { v: int32(100), expectations: func(t *testing.T, b string, err error) { assert.Nil(t, err, "err should be nil") assert.Equal(t, "100", b, "b should equal 100") }, }, { v: int8(100), expectations: func(t *testing.T, b string, err error) { assert.Nil(t, err, "err should be nil") assert.Equal(t, "100", b, "b should equal 100") }, }, { v: uint64(100), expectations: func(t *testing.T, b string, err error) { assert.Nil(t, err, "err should be nil") assert.Equal(t, "100", b, "b should equal 100") }, }, { v: uint32(100), expectations: func(t *testing.T, b string, err error) { assert.Nil(t, err, "err should be nil") assert.Equal(t, "100", b, "b should equal 100") }, }, { v: uint16(100), expectations: func(t *testing.T, b string, err error) { assert.Nil(t, err, "err should be nil") assert.Equal(t, "100", b, "b should equal 100") }, }, { v: uint8(100), expectations: func(t *testing.T, b string, err error) { assert.Nil(t, err, "err should be nil") assert.Equal(t, "100", b, "b should equal 100") }, }, { v: float64(100.12), expectations: func(t *testing.T, b string, err error) { assert.Nil(t, err, "err should be nil") assert.Equal(t, "100.12", b, "b should equal 100.12") }, }, { v: float32(100.12), expectations: func(t *testing.T, b string, err error) { assert.Nil(t, err, "err should be nil") assert.Equal(t, "100.12", b, "b should equal 100.12") }, }, { v: true, expectations: func(t *testing.T, b string, err error) { assert.Nil(t, err, "err should be nil") assert.Equal(t, "true", b, "b should equal true") }, }, { v: "hello world", expectations: func(t *testing.T, b string, err error) { assert.Nil(t, err, "err should be nil") assert.Equal(t, `"hello world"`, b, `b should equal "hello world"`) }, }, { v: "hello world", expectations: func(t *testing.T, b string, err error) { assert.Nil(t, err, "err should be nil") assert.Equal(t, `"hello world"`, b, `b should equal "hello world"`) }, }, { v: &TestEncodingArrStrings{"hello world", "foo bar"}, expectations: func(t *testing.T, b string, err error) { assert.Nil(t, err, "err should be nil") assert.Equal(t, `["hello world","foo bar"]`, b, `b should equal ["hello world","foo bar"]`) }, }, { v: &testObject{ "漢字", nil, 1, nil, 1, nil, 1, nil, 1, nil, 1, nil, 1, nil, 1, nil, 1, nil, 1, nil, 1.1, nil, 1.1, nil, true, nil, &testObject{}, testSliceInts{}, []interface{}{"h", "o", "l", "a"}, }, expectations: func(t *testing.T, b string, err error) { assert.Nil(t, err, "err should be nil") assert.Equal(t, `{"testStr":"漢字","testInt":1,"testInt64":1,"testInt32":1,"testInt16":1,"testInt8":1,"testUint64":1,"testUint32":1,"testUint16":1,"testUint8":1,"testFloat64":1.1,"testFloat32":1.1,"testBool":true}`, string(b), `string(b) should equal {"testStr":"漢字","testInt":1,"testInt64":1,"testInt32":1,"testInt16":1,"testInt8":1,"testUint64":1,"testUint32":1,"testUint16":1,"testUint8":1,"testFloat64":1.1,"testFloat32":1.1,"testBool":true}`) }, }, { v: &struct{}{}, expectations: func(t *testing.T, b string, err error) { assert.NotNil(t, err, "err should be nil") assert.IsType(t, InvalidMarshalError(""), err, "err should be of type InvalidMarshalError") var s = struct{}{} assert.Equal(t, fmt.Sprintf(invalidMarshalErrorMsg, &s), err.Error(), "err message should be equal to invalidMarshalErrorMsg") }, }, } func TestEncoderInterfaceEncodeAPI(t *testing.T) { t.Run("encode-all-types", func(t *testing.T) { for _, test := range encoderTestCases { builder := &strings.Builder{} enc := BorrowEncoder(builder) err := enc.Encode(test.v) enc.Release() test.expectations(t, builder.String(), err) } }) t.Run("encode-all-types-write-error", func(t *testing.T) { v := "" w := TestWriterError("") enc := BorrowEncoder(w) err := enc.Encode(v) assert.NotNil(t, err, "err should not be nil") }) t.Run("encode-all-types-pool-error", func(t *testing.T) { v := "" w := TestWriterError("") enc := BorrowEncoder(w) enc.isPooled = 1 defer func() { err := recover() assert.NotNil(t, err, "err should not be nil") assert.IsType(t, InvalidUsagePooledEncoderError(""), err, "err should be of type InvalidUsagePooledEncoderError") }() _ = enc.Encode(v) assert.True(t, false, "should not be called as decoder should have panicked") }) } func TestEncoderInterfaceMarshalAPI(t *testing.T) { t.Run("marshal-all-types", func(t *testing.T) { for _, test := range encoderTestCases { b, err := Marshal(test.v) test.expectations(t, string(b), err) } }) } golang-github-francoispqt-gojay-1.2.13/encode_null.go000066400000000000000000000015571365331665100226220ustar00rootroot00000000000000package gojay // AddNull adds a `null` to be encoded. Must be used while encoding an array.` func (enc *Encoder) AddNull() { enc.Null() } // Null adds a `null` to be encoded. Must be used while encoding an array.` func (enc *Encoder) Null() { enc.grow(5) r := enc.getPreviousRune() if r != '[' { enc.writeByte(',') } enc.writeBytes(nullBytes) } // AddNullKey adds a `null` to be encoded. Must be used while encoding an array.` func (enc *Encoder) AddNullKey(key string) { enc.NullKey(key) } // NullKey adds a `null` to be encoded. Must be used while encoding an array.` func (enc *Encoder) NullKey(key string) { if enc.hasKeys { if !enc.keyExists(key) { return } } enc.grow(5 + len(key)) r := enc.getPreviousRune() if r != '{' { enc.writeByte(',') } enc.writeByte('"') enc.writeStringEscape(key) enc.writeBytes(objKey) enc.writeBytes(nullBytes) } golang-github-francoispqt-gojay-1.2.13/encode_null_test.go000066400000000000000000000026101365331665100236500ustar00rootroot00000000000000package gojay import ( "strings" "testing" "github.com/stretchr/testify/assert" ) func TestEncodeNull(t *testing.T) { var testCases = []struct { name string baseJSON string expectedJSON string }{ { name: "basic 1st element", baseJSON: `[`, expectedJSON: `[null,null`, }, { name: "basic last element", baseJSON: `["test"`, expectedJSON: `["test",null,null`, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { var b strings.Builder var enc = NewEncoder(&b) enc.writeString(testCase.baseJSON) enc.Null() enc.AddNull() enc.Write() assert.Equal(t, testCase.expectedJSON, b.String()) }) } } func TestEncodeNullKey(t *testing.T) { var testCases = []struct { name string baseJSON string expectedJSON string }{ { name: "basic 1st element", baseJSON: `{`, expectedJSON: `{"foo":null,"bar":null`, }, { name: "basic last element", baseJSON: `{"test":"test"`, expectedJSON: `{"test":"test","foo":null,"bar":null`, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { var b strings.Builder var enc = NewEncoder(&b) enc.writeString(testCase.baseJSON) enc.NullKey("foo") enc.AddNullKey("bar") enc.Write() assert.Equal(t, testCase.expectedJSON, b.String()) }) } } golang-github-francoispqt-gojay-1.2.13/encode_number.go000066400000000000000000000000161365331665100231250ustar00rootroot00000000000000package gojay golang-github-francoispqt-gojay-1.2.13/encode_number_float.go000066400000000000000000000252311365331665100243200ustar00rootroot00000000000000package gojay import "strconv" // EncodeFloat encodes a float64 to JSON func (enc *Encoder) EncodeFloat(n float64) error { if enc.isPooled == 1 { panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder")) } _, _ = enc.encodeFloat(n) _, err := enc.Write() if err != nil { return err } return nil } // encodeFloat encodes a float64 to JSON func (enc *Encoder) encodeFloat(n float64) ([]byte, error) { enc.buf = strconv.AppendFloat(enc.buf, n, 'f', -1, 64) return enc.buf, nil } // EncodeFloat32 encodes a float32 to JSON func (enc *Encoder) EncodeFloat32(n float32) error { if enc.isPooled == 1 { panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder")) } _, _ = enc.encodeFloat32(n) _, err := enc.Write() if err != nil { return err } return nil } func (enc *Encoder) encodeFloat32(n float32) ([]byte, error) { enc.buf = strconv.AppendFloat(enc.buf, float64(n), 'f', -1, 32) return enc.buf, nil } // AddFloat adds a float64 to be encoded, must be used inside a slice or array encoding (does not encode a key) func (enc *Encoder) AddFloat(v float64) { enc.Float64(v) } // AddFloatOmitEmpty adds a float64 to be encoded and skips it if its value is 0, // must be used inside a slice or array encoding (does not encode a key). func (enc *Encoder) AddFloatOmitEmpty(v float64) { enc.Float64OmitEmpty(v) } // AddFloatNullEmpty adds a float64 to be encoded and skips it if its value is 0, // must be used inside a slice or array encoding (does not encode a key). func (enc *Encoder) AddFloatNullEmpty(v float64) { enc.Float64NullEmpty(v) } // Float adds a float64 to be encoded, must be used inside a slice or array encoding (does not encode a key) func (enc *Encoder) Float(v float64) { enc.Float64(v) } // FloatOmitEmpty adds a float64 to be encoded and skips it if its value is 0, // must be used inside a slice or array encoding (does not encode a key). func (enc *Encoder) FloatOmitEmpty(v float64) { enc.Float64OmitEmpty(v) } // FloatNullEmpty adds a float64 to be encoded and skips it if its value is 0, // must be used inside a slice or array encoding (does not encode a key). func (enc *Encoder) FloatNullEmpty(v float64) { enc.Float64NullEmpty(v) } // AddFloatKey adds a float64 to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) AddFloatKey(key string, v float64) { enc.Float64Key(key, v) } // AddFloatKeyOmitEmpty adds a float64 to be encoded and skips it if its value is 0. // Must be used inside an object as it will encode a key func (enc *Encoder) AddFloatKeyOmitEmpty(key string, v float64) { enc.Float64KeyOmitEmpty(key, v) } // AddFloatKeyNullEmpty adds a float64 to be encoded and skips it if its value is 0. // Must be used inside an object as it will encode a key func (enc *Encoder) AddFloatKeyNullEmpty(key string, v float64) { enc.Float64KeyNullEmpty(key, v) } // FloatKey adds a float64 to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) FloatKey(key string, v float64) { enc.Float64Key(key, v) } // FloatKeyOmitEmpty adds a float64 to be encoded and skips it if its value is 0. // Must be used inside an object as it will encode a key func (enc *Encoder) FloatKeyOmitEmpty(key string, v float64) { enc.Float64KeyOmitEmpty(key, v) } // FloatKeyNullEmpty adds a float64 to be encoded and skips it if its value is 0. // Must be used inside an object as it will encode a key func (enc *Encoder) FloatKeyNullEmpty(key string, v float64) { enc.Float64KeyNullEmpty(key, v) } // AddFloat64 adds a float64 to be encoded, must be used inside a slice or array encoding (does not encode a key) func (enc *Encoder) AddFloat64(v float64) { enc.Float(v) } // AddFloat64OmitEmpty adds a float64 to be encoded and skips it if its value is 0, // must be used inside a slice or array encoding (does not encode a key). func (enc *Encoder) AddFloat64OmitEmpty(v float64) { enc.FloatOmitEmpty(v) } // Float64 adds a float64 to be encoded, must be used inside a slice or array encoding (does not encode a key) func (enc *Encoder) Float64(v float64) { enc.grow(10) r := enc.getPreviousRune() if r != '[' { enc.writeByte(',') } enc.buf = strconv.AppendFloat(enc.buf, v, 'f', -1, 64) } // Float64OmitEmpty adds a float64 to be encoded and skips it if its value is 0, // must be used inside a slice or array encoding (does not encode a key). func (enc *Encoder) Float64OmitEmpty(v float64) { if v == 0 { return } enc.grow(10) r := enc.getPreviousRune() if r != '[' { enc.writeByte(',') } enc.buf = strconv.AppendFloat(enc.buf, v, 'f', -1, 64) } // Float64NullEmpty adds a float64 to be encoded and skips it if its value is 0, // must be used inside a slice or array encoding (does not encode a key). func (enc *Encoder) Float64NullEmpty(v float64) { enc.grow(10) r := enc.getPreviousRune() if r != '[' { enc.writeByte(',') } if v == 0 { enc.writeBytes(nullBytes) return } enc.buf = strconv.AppendFloat(enc.buf, v, 'f', -1, 64) } // AddFloat64Key adds a float64 to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) AddFloat64Key(key string, v float64) { enc.FloatKey(key, v) } // AddFloat64KeyOmitEmpty adds a float64 to be encoded and skips it if its value is 0. // Must be used inside an object as it will encode a key func (enc *Encoder) AddFloat64KeyOmitEmpty(key string, v float64) { enc.FloatKeyOmitEmpty(key, v) } // Float64Key adds a float64 to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) Float64Key(key string, value float64) { if enc.hasKeys { if !enc.keyExists(key) { return } } r := enc.getPreviousRune() if r != '{' { enc.writeByte(',') } enc.grow(10) enc.writeByte('"') enc.writeStringEscape(key) enc.writeBytes(objKey) enc.buf = strconv.AppendFloat(enc.buf, value, 'f', -1, 64) } // Float64KeyOmitEmpty adds a float64 to be encoded and skips it if its value is 0. // Must be used inside an object as it will encode a key func (enc *Encoder) Float64KeyOmitEmpty(key string, v float64) { if enc.hasKeys { if !enc.keyExists(key) { return } } if v == 0 { return } enc.grow(10 + len(key)) r := enc.getPreviousRune() if r != '{' { enc.writeByte(',') } enc.writeByte('"') enc.writeStringEscape(key) enc.writeBytes(objKey) enc.buf = strconv.AppendFloat(enc.buf, v, 'f', -1, 64) } // Float64KeyNullEmpty adds a float64 to be encoded and skips it if its value is 0, // must be used inside a slice or array encoding (does not encode a key). func (enc *Encoder) Float64KeyNullEmpty(key string, v float64) { if enc.hasKeys { if !enc.keyExists(key) { return } } enc.grow(10 + len(key)) r := enc.getPreviousRune() if r != '{' { enc.writeByte(',') } enc.writeByte('"') enc.writeStringEscape(key) enc.writeBytes(objKey) if v == 0 { enc.writeBytes(nullBytes) return } enc.buf = strconv.AppendFloat(enc.buf, v, 'f', -1, 64) } // AddFloat32 adds a float32 to be encoded, must be used inside a slice or array encoding (does not encode a key) func (enc *Encoder) AddFloat32(v float32) { enc.Float32(v) } // AddFloat32OmitEmpty adds an int to be encoded and skips it if its value is 0, // must be used inside a slice or array encoding (does not encode a key). func (enc *Encoder) AddFloat32OmitEmpty(v float32) { enc.Float32OmitEmpty(v) } // AddFloat32NullEmpty adds an int to be encoded and skips it if its value is 0, // must be used inside a slice or array encoding (does not encode a key). func (enc *Encoder) AddFloat32NullEmpty(v float32) { enc.Float32NullEmpty(v) } // Float32 adds a float32 to be encoded, must be used inside a slice or array encoding (does not encode a key) func (enc *Encoder) Float32(v float32) { r := enc.getPreviousRune() if r != '[' { enc.writeByte(',') } enc.buf = strconv.AppendFloat(enc.buf, float64(v), 'f', -1, 32) } // Float32OmitEmpty adds an int to be encoded and skips it if its value is 0, // must be used inside a slice or array encoding (does not encode a key). func (enc *Encoder) Float32OmitEmpty(v float32) { if v == 0 { return } enc.grow(10) r := enc.getPreviousRune() if r != '[' { enc.writeByte(',') } enc.buf = strconv.AppendFloat(enc.buf, float64(v), 'f', -1, 32) } // Float32NullEmpty adds an int to be encoded and skips it if its value is 0, // must be used inside a slice or array encoding (does not encode a key). func (enc *Encoder) Float32NullEmpty(v float32) { enc.grow(10) r := enc.getPreviousRune() if r != '[' { enc.writeByte(',') } if v == 0 { enc.writeBytes(nullBytes) return } enc.buf = strconv.AppendFloat(enc.buf, float64(v), 'f', -1, 32) } // AddFloat32Key adds a float32 to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) AddFloat32Key(key string, v float32) { enc.Float32Key(key, v) } // AddFloat32KeyOmitEmpty adds a float64 to be encoded and skips it if its value is 0. // Must be used inside an object as it will encode a key func (enc *Encoder) AddFloat32KeyOmitEmpty(key string, v float32) { enc.Float32KeyOmitEmpty(key, v) } // AddFloat32KeyNullEmpty adds a float64 to be encoded and skips it if its value is 0. // Must be used inside an object as it will encode a key func (enc *Encoder) AddFloat32KeyNullEmpty(key string, v float32) { enc.Float32KeyNullEmpty(key, v) } // Float32Key adds a float32 to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) Float32Key(key string, v float32) { if enc.hasKeys { if !enc.keyExists(key) { return } } enc.grow(10 + len(key)) r := enc.getPreviousRune() if r != '{' { enc.writeByte(',') } enc.writeByte('"') enc.writeStringEscape(key) enc.writeByte('"') enc.writeByte(':') enc.buf = strconv.AppendFloat(enc.buf, float64(v), 'f', -1, 32) } // Float32KeyOmitEmpty adds a float64 to be encoded and skips it if its value is 0. // Must be used inside an object as it will encode a key func (enc *Encoder) Float32KeyOmitEmpty(key string, v float32) { if enc.hasKeys { if !enc.keyExists(key) { return } } if v == 0 { return } enc.grow(10 + len(key)) r := enc.getPreviousRune() if r != '{' { enc.writeByte(',') } enc.writeByte('"') enc.writeStringEscape(key) enc.writeBytes(objKey) enc.buf = strconv.AppendFloat(enc.buf, float64(v), 'f', -1, 32) } // Float32KeyNullEmpty adds a float64 to be encoded and skips it if its value is 0. // Must be used inside an object as it will encode a key func (enc *Encoder) Float32KeyNullEmpty(key string, v float32) { if enc.hasKeys { if !enc.keyExists(key) { return } } enc.grow(10 + len(key)) r := enc.getPreviousRune() if r != '{' { enc.writeByte(',') } enc.writeByte('"') enc.writeStringEscape(key) enc.writeBytes(objKey) if v == 0 { enc.writeBytes(nullBytes) return } enc.buf = strconv.AppendFloat(enc.buf, float64(v), 'f', -1, 32) } golang-github-francoispqt-gojay-1.2.13/encode_number_float_test.go000066400000000000000000000125021365331665100253540ustar00rootroot00000000000000package gojay import ( "strings" "testing" "github.com/stretchr/testify/assert" ) func TestEncoderFloat64(t *testing.T) { var testCasesBasic = []struct { name string v float64 expectedJSON string }{ { name: "basic", v: float64(1), expectedJSON: "[1,1]", }, { name: "big", v: float64(0), expectedJSON: "[0,0]", }, } for _, testCase := range testCasesBasic { t.Run(testCase.name, func(t *testing.T) { var b = &strings.Builder{} var enc = NewEncoder(b) enc.Encode(EncodeArrayFunc(func(enc *Encoder) { enc.Float64(testCase.v) enc.AddFloat64(testCase.v) })) assert.Equal(t, testCase.expectedJSON, b.String()) }) } var testCasesOmitEmpty = []struct { name string v float64 expectedJSON string }{ { name: "basic", v: float64(1), expectedJSON: "[1,1]", }, { name: "big", v: float64(0), expectedJSON: "[]", }, } for _, testCase := range testCasesOmitEmpty { t.Run(testCase.name, func(t *testing.T) { var b = &strings.Builder{} var enc = NewEncoder(b) enc.Encode(EncodeArrayFunc(func(enc *Encoder) { enc.Float64OmitEmpty(testCase.v) enc.AddFloat64OmitEmpty(testCase.v) })) assert.Equal(t, testCase.expectedJSON, b.String()) }) } var testCasesKeyBasic = []struct { name string v float64 expectedJSON string }{ { name: "basic", v: float64(1), expectedJSON: `{"foo":1,"bar":1}`, }, { name: "big", v: float64(0), expectedJSON: `{"foo":0,"bar":0}`, }, } for _, testCase := range testCasesKeyBasic { t.Run(testCase.name, func(t *testing.T) { var b = &strings.Builder{} var enc = NewEncoder(b) enc.Encode(EncodeObjectFunc(func(enc *Encoder) { enc.Float64Key("foo", testCase.v) enc.AddFloat64Key("bar", testCase.v) })) assert.Equal(t, testCase.expectedJSON, b.String()) }) } var testCasesKeyOmitEmpty = []struct { name string v float64 expectedJSON string }{ { name: "basic", v: float64(1), expectedJSON: `{"foo":1,"bar":1}`, }, { name: "big", v: float64(0), expectedJSON: "{}", }, } for _, testCase := range testCasesKeyOmitEmpty { t.Run(testCase.name, func(t *testing.T) { var b = &strings.Builder{} var enc = NewEncoder(b) enc.Encode(EncodeObjectFunc(func(enc *Encoder) { enc.Float64KeyOmitEmpty("foo", testCase.v) enc.AddFloat64KeyOmitEmpty("bar", testCase.v) })) assert.Equal(t, testCase.expectedJSON, b.String()) }) } } func TestEncoderFloat64NullEmpty(t *testing.T) { var testCases = []struct { name string baseJSON string expectedJSON string }{ { name: "basic 1st elem", baseJSON: "[", expectedJSON: `[null,1`, }, { name: "basic 2nd elem", baseJSON: `["test"`, expectedJSON: `["test",null,1`, }, } for _, testCase := range testCases { t.Run("true", func(t *testing.T) { var b strings.Builder var enc = NewEncoder(&b) enc.writeString(testCase.baseJSON) enc.FloatNullEmpty(0) enc.AddFloatNullEmpty(1) enc.Write() assert.Equal(t, testCase.expectedJSON, b.String()) }) } } func TestEncoderFloat64KeyNullEmpty(t *testing.T) { var testCases = []struct { name string baseJSON string expectedJSON string }{ { name: "basic 1st elem", baseJSON: "{", expectedJSON: `{"foo":null,"bar":1`, }, { name: "basic 2nd elem", baseJSON: `{"test":"test"`, expectedJSON: `{"test":"test","foo":null,"bar":1`, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { var b strings.Builder var enc = NewEncoder(&b) enc.writeString(testCase.baseJSON) enc.FloatKeyNullEmpty("foo", 0) enc.AddFloatKeyNullEmpty("bar", 1) enc.Write() assert.Equal(t, testCase.expectedJSON, b.String()) }) } } func TestEncoderFloat32NullEmpty(t *testing.T) { var testCases = []struct { name string baseJSON string expectedJSON string }{ { name: "basic 1st elem", baseJSON: "[", expectedJSON: `[null,1`, }, { name: "basic 2nd elem", baseJSON: `["test"`, expectedJSON: `["test",null,1`, }, } for _, testCase := range testCases { t.Run("true", func(t *testing.T) { var b strings.Builder var enc = NewEncoder(&b) enc.writeString(testCase.baseJSON) enc.Float32NullEmpty(0) enc.AddFloat32NullEmpty(1) enc.Write() assert.Equal(t, testCase.expectedJSON, b.String()) }) } } func TestEncoderFloat32KeyNullEmpty(t *testing.T) { var testCases = []struct { name string baseJSON string expectedJSON string }{ { name: "basic 1st elem", baseJSON: "{", expectedJSON: `{"foo":null,"bar":1`, }, { name: "basic 2nd elem", baseJSON: `{"test":"test"`, expectedJSON: `{"test":"test","foo":null,"bar":1`, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { var b strings.Builder var enc = NewEncoder(&b) enc.writeString(testCase.baseJSON) enc.Float32KeyNullEmpty("foo", 0) enc.AddFloat32KeyNullEmpty("bar", 1) enc.Write() assert.Equal(t, testCase.expectedJSON, b.String()) }) } } golang-github-francoispqt-gojay-1.2.13/encode_number_int.go000066400000000000000000000363531365331665100240140ustar00rootroot00000000000000package gojay import "strconv" // EncodeInt encodes an int to JSON func (enc *Encoder) EncodeInt(n int) error { if enc.isPooled == 1 { panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder")) } _, _ = enc.encodeInt(n) _, err := enc.Write() if err != nil { return err } return nil } // encodeInt encodes an int to JSON func (enc *Encoder) encodeInt(n int) ([]byte, error) { enc.buf = strconv.AppendInt(enc.buf, int64(n), 10) return enc.buf, nil } // EncodeInt64 encodes an int64 to JSON func (enc *Encoder) EncodeInt64(n int64) error { if enc.isPooled == 1 { panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder")) } _, _ = enc.encodeInt64(n) _, err := enc.Write() if err != nil { return err } return nil } // encodeInt64 encodes an int to JSON func (enc *Encoder) encodeInt64(n int64) ([]byte, error) { enc.buf = strconv.AppendInt(enc.buf, n, 10) return enc.buf, nil } // AddInt adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key) func (enc *Encoder) AddInt(v int) { enc.Int(v) } // AddIntOmitEmpty adds an int to be encoded and skips it if its value is 0, // must be used inside a slice or array encoding (does not encode a key). func (enc *Encoder) AddIntOmitEmpty(v int) { enc.IntOmitEmpty(v) } // AddIntNullEmpty adds an int to be encoded and skips it if its value is 0, // must be used inside a slice or array encoding (does not encode a key). func (enc *Encoder) AddIntNullEmpty(v int) { enc.IntNullEmpty(v) } // Int adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key) func (enc *Encoder) Int(v int) { enc.grow(10) r := enc.getPreviousRune() if r != '[' { enc.writeByte(',') } enc.buf = strconv.AppendInt(enc.buf, int64(v), 10) } // IntOmitEmpty adds an int to be encoded and skips it if its value is 0, // must be used inside a slice or array encoding (does not encode a key). func (enc *Encoder) IntOmitEmpty(v int) { if v == 0 { return } enc.grow(10) r := enc.getPreviousRune() if r != '[' { enc.writeByte(',') } enc.buf = strconv.AppendInt(enc.buf, int64(v), 10) } // IntNullEmpty adds an int to be encoded and skips it if its value is 0, // must be used inside a slice or array encoding (does not encode a key). func (enc *Encoder) IntNullEmpty(v int) { enc.grow(10) r := enc.getPreviousRune() if r != '[' { enc.writeByte(',') } if v == 0 { enc.writeBytes(nullBytes) return } enc.buf = strconv.AppendInt(enc.buf, int64(v), 10) } // AddIntKey adds an int to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) AddIntKey(key string, v int) { enc.IntKey(key, v) } // AddIntKeyOmitEmpty adds an int to be encoded and skips it if its value is 0. // Must be used inside an object as it will encode a key. func (enc *Encoder) AddIntKeyOmitEmpty(key string, v int) { enc.IntKeyOmitEmpty(key, v) } // AddIntKeyNullEmpty adds an int to be encoded and skips it if its value is 0. // Must be used inside an object as it will encode a key. func (enc *Encoder) AddIntKeyNullEmpty(key string, v int) { enc.IntKeyNullEmpty(key, v) } // IntKey adds an int to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) IntKey(key string, v int) { if enc.hasKeys { if !enc.keyExists(key) { return } } enc.grow(10 + len(key)) r := enc.getPreviousRune() if r != '{' { enc.writeByte(',') } enc.writeByte('"') enc.writeStringEscape(key) enc.writeBytes(objKey) enc.buf = strconv.AppendInt(enc.buf, int64(v), 10) } // IntKeyOmitEmpty adds an int to be encoded and skips it if its value is 0. // Must be used inside an object as it will encode a key. func (enc *Encoder) IntKeyOmitEmpty(key string, v int) { if enc.hasKeys { if !enc.keyExists(key) { return } } if v == 0 { return } enc.grow(10 + len(key)) r := enc.getPreviousRune() if r != '{' && r != '[' { enc.writeByte(',') } enc.writeByte('"') enc.writeStringEscape(key) enc.writeBytes(objKey) enc.buf = strconv.AppendInt(enc.buf, int64(v), 10) } // IntKeyNullEmpty adds an int to be encoded and skips it if its value is 0. // Must be used inside an object as it will encode a key. func (enc *Encoder) IntKeyNullEmpty(key string, v int) { if enc.hasKeys { if !enc.keyExists(key) { return } } enc.grow(10 + len(key)) r := enc.getPreviousRune() if r != '{' && r != '[' { enc.writeByte(',') } enc.writeByte('"') enc.writeStringEscape(key) enc.writeBytes(objKey) if v == 0 { enc.writeBytes(nullBytes) return } enc.buf = strconv.AppendInt(enc.buf, int64(v), 10) } // AddInt64 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key) func (enc *Encoder) AddInt64(v int64) { enc.Int64(v) } // AddInt64OmitEmpty adds an int to be encoded and skips it if its value is 0, // must be used inside a slice or array encoding (does not encode a key). func (enc *Encoder) AddInt64OmitEmpty(v int64) { enc.Int64OmitEmpty(v) } // AddInt64NullEmpty adds an int to be encoded and skips it if its value is 0, // must be used inside a slice or array encoding (does not encode a key). func (enc *Encoder) AddInt64NullEmpty(v int64) { enc.Int64NullEmpty(v) } // Int64 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key) func (enc *Encoder) Int64(v int64) { enc.grow(10) r := enc.getPreviousRune() if r != '[' { enc.writeByte(',') } enc.buf = strconv.AppendInt(enc.buf, v, 10) } // Int64OmitEmpty adds an int to be encoded and skips it if its value is 0, // must be used inside a slice or array encoding (does not encode a key). func (enc *Encoder) Int64OmitEmpty(v int64) { if v == 0 { return } enc.grow(10) r := enc.getPreviousRune() if r != '[' { enc.writeByte(',') } enc.buf = strconv.AppendInt(enc.buf, v, 10) } // Int64NullEmpty adds an int to be encoded and skips it if its value is 0, // must be used inside a slice or array encoding (does not encode a key). func (enc *Encoder) Int64NullEmpty(v int64) { enc.grow(10) r := enc.getPreviousRune() if r != '[' { enc.writeByte(',') } if v == 0 { enc.writeBytes(nullBytes) return } enc.buf = strconv.AppendInt(enc.buf, v, 10) } // AddInt64Key adds an int64 to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) AddInt64Key(key string, v int64) { enc.Int64Key(key, v) } // AddInt64KeyOmitEmpty adds an int64 to be encoded and skips it if its value is 0. // Must be used inside an object as it will encode a key. func (enc *Encoder) AddInt64KeyOmitEmpty(key string, v int64) { enc.Int64KeyOmitEmpty(key, v) } // AddInt64KeyNullEmpty adds an int64 to be encoded and skips it if its value is 0. // Must be used inside an object as it will encode a key. func (enc *Encoder) AddInt64KeyNullEmpty(key string, v int64) { enc.Int64KeyNullEmpty(key, v) } // Int64Key adds an int64 to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) Int64Key(key string, v int64) { if enc.hasKeys { if !enc.keyExists(key) { return } } enc.grow(10 + len(key)) r := enc.getPreviousRune() if r != '{' { enc.writeByte(',') } enc.writeByte('"') enc.writeStringEscape(key) enc.writeBytes(objKey) enc.buf = strconv.AppendInt(enc.buf, v, 10) } // Int64KeyOmitEmpty adds an int64 to be encoded and skips it if its value is 0. // Must be used inside an object as it will encode a key. func (enc *Encoder) Int64KeyOmitEmpty(key string, v int64) { if v == 0 { return } enc.grow(10 + len(key)) r := enc.getPreviousRune() if r != '{' { enc.writeByte(',') } enc.writeByte('"') enc.writeStringEscape(key) enc.writeBytes(objKey) enc.buf = strconv.AppendInt(enc.buf, v, 10) } // Int64KeyNullEmpty adds an int64 to be encoded and skips it if its value is 0. // Must be used inside an object as it will encode a key. func (enc *Encoder) Int64KeyNullEmpty(key string, v int64) { if enc.hasKeys { if !enc.keyExists(key) { return } } enc.grow(10 + len(key)) r := enc.getPreviousRune() if r != '{' { enc.writeByte(',') } enc.writeByte('"') enc.writeStringEscape(key) enc.writeBytes(objKey) if v == 0 { enc.writeBytes(nullBytes) return } enc.buf = strconv.AppendInt(enc.buf, v, 10) } // AddInt32 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key) func (enc *Encoder) AddInt32(v int32) { enc.Int64(int64(v)) } // AddInt32OmitEmpty adds an int to be encoded and skips it if its value is 0, // must be used inside a slice or array encoding (does not encode a key). func (enc *Encoder) AddInt32OmitEmpty(v int32) { enc.Int64OmitEmpty(int64(v)) } // AddInt32NullEmpty adds an int to be encoded and skips it if its value is 0, // must be used inside a slice or array encoding (does not encode a key). func (enc *Encoder) AddInt32NullEmpty(v int32) { enc.Int64NullEmpty(int64(v)) } // Int32 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key) func (enc *Encoder) Int32(v int32) { enc.Int64(int64(v)) } // Int32OmitEmpty adds an int to be encoded and skips it if its value is 0, // must be used inside a slice or array encoding (does not encode a key). func (enc *Encoder) Int32OmitEmpty(v int32) { enc.Int64OmitEmpty(int64(v)) } // Int32NullEmpty adds an int to be encoded and skips it if its value is 0, // must be used inside a slice or array encoding (does not encode a key). func (enc *Encoder) Int32NullEmpty(v int32) { enc.Int64NullEmpty(int64(v)) } // AddInt32Key adds an int32 to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) AddInt32Key(key string, v int32) { enc.Int64Key(key, int64(v)) } // AddInt32KeyOmitEmpty adds an int32 to be encoded and skips it if its value is 0. // Must be used inside an object as it will encode a key. func (enc *Encoder) AddInt32KeyOmitEmpty(key string, v int32) { enc.Int64KeyOmitEmpty(key, int64(v)) } // Int32Key adds an int32 to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) Int32Key(key string, v int32) { enc.Int64Key(key, int64(v)) } // Int32KeyOmitEmpty adds an int32 to be encoded and skips it if its value is 0. // Must be used inside an object as it will encode a key. func (enc *Encoder) Int32KeyOmitEmpty(key string, v int32) { enc.Int64KeyOmitEmpty(key, int64(v)) } // Int32KeyNullEmpty adds an int32 to be encoded and skips it if its value is 0. // Must be used inside an object as it will encode a key. func (enc *Encoder) Int32KeyNullEmpty(key string, v int32) { enc.Int64KeyNullEmpty(key, int64(v)) } // AddInt16 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key) func (enc *Encoder) AddInt16(v int16) { enc.Int64(int64(v)) } // AddInt16OmitEmpty adds an int to be encoded and skips it if its value is 0, // must be used inside a slice or array encoding (does not encode a key). func (enc *Encoder) AddInt16OmitEmpty(v int16) { enc.Int64OmitEmpty(int64(v)) } // Int16 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key) func (enc *Encoder) Int16(v int16) { enc.Int64(int64(v)) } // Int16OmitEmpty adds an int to be encoded and skips it if its value is 0, // must be used inside a slice or array encoding (does not encode a key). func (enc *Encoder) Int16OmitEmpty(v int16) { enc.Int64OmitEmpty(int64(v)) } // Int16NullEmpty adds an int to be encoded and skips it if its value is 0, // must be used inside a slice or array encoding (does not encode a key). func (enc *Encoder) Int16NullEmpty(v int16) { enc.Int64NullEmpty(int64(v)) } // AddInt16Key adds an int16 to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) AddInt16Key(key string, v int16) { enc.Int64Key(key, int64(v)) } // AddInt16KeyOmitEmpty adds an int16 to be encoded and skips it if its value is 0. // Must be used inside an object as it will encode a key. func (enc *Encoder) AddInt16KeyOmitEmpty(key string, v int16) { enc.Int64KeyOmitEmpty(key, int64(v)) } // AddInt16KeyNullEmpty adds an int16 to be encoded and skips it if its value is 0. // Must be used inside an object as it will encode a key. func (enc *Encoder) AddInt16KeyNullEmpty(key string, v int16) { enc.Int64KeyNullEmpty(key, int64(v)) } // Int16Key adds an int16 to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) Int16Key(key string, v int16) { enc.Int64Key(key, int64(v)) } // Int16KeyOmitEmpty adds an int16 to be encoded and skips it if its value is 0. // Must be used inside an object as it will encode a key. func (enc *Encoder) Int16KeyOmitEmpty(key string, v int16) { enc.Int64KeyOmitEmpty(key, int64(v)) } // Int16KeyNullEmpty adds an int16 to be encoded and skips it if its value is 0. // Must be used inside an object as it will encode a key. func (enc *Encoder) Int16KeyNullEmpty(key string, v int16) { enc.Int64KeyNullEmpty(key, int64(v)) } // AddInt8 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key) func (enc *Encoder) AddInt8(v int8) { enc.Int64(int64(v)) } // AddInt8OmitEmpty adds an int to be encoded and skips it if its value is 0, // must be used inside a slice or array encoding (does not encode a key). func (enc *Encoder) AddInt8OmitEmpty(v int8) { enc.Int64OmitEmpty(int64(v)) } // AddInt8NullEmpty adds an int to be encoded and skips it if its value is 0, // must be used inside a slice or array encoding (does not encode a key). func (enc *Encoder) AddInt8NullEmpty(v int8) { enc.Int64NullEmpty(int64(v)) } // Int8 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key) func (enc *Encoder) Int8(v int8) { enc.Int64(int64(v)) } // Int8OmitEmpty adds an int to be encoded and skips it if its value is 0, // must be used inside a slice or array encoding (does not encode a key). func (enc *Encoder) Int8OmitEmpty(v int8) { enc.Int64OmitEmpty(int64(v)) } // Int8NullEmpty adds an int to be encoded and skips it if its value is 0, // must be used inside a slice or array encoding (does not encode a key). func (enc *Encoder) Int8NullEmpty(v int8) { enc.Int64NullEmpty(int64(v)) } // AddInt8Key adds an int8 to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) AddInt8Key(key string, v int8) { enc.Int64Key(key, int64(v)) } // AddInt8KeyOmitEmpty adds an int8 to be encoded and skips it if its value is 0. // Must be used inside an object as it will encode a key. func (enc *Encoder) AddInt8KeyOmitEmpty(key string, v int8) { enc.Int64KeyOmitEmpty(key, int64(v)) } // AddInt8KeyNullEmpty adds an int8 to be encoded and skips it if its value is 0. // Must be used inside an object as it will encode a key. func (enc *Encoder) AddInt8KeyNullEmpty(key string, v int8) { enc.Int64KeyNullEmpty(key, int64(v)) } // Int8Key adds an int8 to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) Int8Key(key string, v int8) { enc.Int64Key(key, int64(v)) } // Int8KeyOmitEmpty adds an int8 to be encoded and skips it if its value is 0. // Must be used inside an object as it will encode a key. func (enc *Encoder) Int8KeyOmitEmpty(key string, v int8) { enc.Int64KeyOmitEmpty(key, int64(v)) } // Int8KeyNullEmpty adds an int8 to be encoded and skips it if its value is 0. // Must be used inside an object as it will encode a key. func (enc *Encoder) Int8KeyNullEmpty(key string, v int8) { enc.Int64KeyNullEmpty(key, int64(v)) } golang-github-francoispqt-gojay-1.2.13/encode_number_int_test.go000066400000000000000000000450471365331665100250530ustar00rootroot00000000000000package gojay import ( "math" "strings" "testing" "github.com/stretchr/testify/assert" ) func TestEncoderInt64(t *testing.T) { var testCasesBasic = []struct { name string v int64 expectedJSON string }{ { name: "basic", v: int64(1), expectedJSON: "[1,1]", }, { name: "big", v: math.MaxInt64, expectedJSON: "[9223372036854775807,9223372036854775807]", }, { name: "big", v: int64(0), expectedJSON: "[0,0]", }, } for _, testCase := range testCasesBasic { t.Run(testCase.name, func(t *testing.T) { var b = &strings.Builder{} var enc = NewEncoder(b) enc.Encode(EncodeArrayFunc(func(enc *Encoder) { enc.Int64(testCase.v) enc.AddInt64(testCase.v) })) assert.Equal(t, testCase.expectedJSON, b.String()) }) } var testCasesOmitEmpty = []struct { name string v int64 expectedJSON string }{ { name: "basic", v: int64(1), expectedJSON: "[1,1]", }, { name: "big", v: math.MaxInt64, expectedJSON: "[9223372036854775807,9223372036854775807]", }, { name: "big", v: int64(0), expectedJSON: "[]", }, } for _, testCase := range testCasesOmitEmpty { t.Run(testCase.name, func(t *testing.T) { var b = &strings.Builder{} var enc = NewEncoder(b) enc.Encode(EncodeArrayFunc(func(enc *Encoder) { enc.Int64OmitEmpty(testCase.v) enc.AddInt64OmitEmpty(testCase.v) })) assert.Equal(t, testCase.expectedJSON, b.String()) }) } var testCasesKeyBasic = []struct { name string v int64 expectedJSON string }{ { name: "basic", v: int64(1), expectedJSON: `{"foo":1,"bar":1}`, }, { name: "big", v: math.MaxInt64, expectedJSON: `{"foo":9223372036854775807,"bar":9223372036854775807}`, }, { name: "big", v: int64(0), expectedJSON: `{"foo":0,"bar":0}`, }, } for _, testCase := range testCasesKeyBasic { t.Run(testCase.name, func(t *testing.T) { var b = &strings.Builder{} var enc = NewEncoder(b) enc.Encode(EncodeObjectFunc(func(enc *Encoder) { enc.Int64Key("foo", testCase.v) enc.AddInt64Key("bar", testCase.v) })) assert.Equal(t, testCase.expectedJSON, b.String()) }) } var testCasesKeyOmitEmpty = []struct { name string v int64 expectedJSON string }{ { name: "basic", v: int64(1), expectedJSON: `{"foo":1,"bar":1}`, }, { name: "big", v: math.MaxInt64, expectedJSON: `{"foo":9223372036854775807,"bar":9223372036854775807}`, }, { name: "big", v: int64(0), expectedJSON: `{}`, }, } for _, testCase := range testCasesKeyOmitEmpty { t.Run(testCase.name, func(t *testing.T) { var b = &strings.Builder{} var enc = NewEncoder(b) enc.Encode(EncodeObjectFunc(func(enc *Encoder) { enc.Int64KeyOmitEmpty("foo", testCase.v) enc.AddInt64KeyOmitEmpty("bar", testCase.v) })) assert.Equal(t, testCase.expectedJSON, b.String()) }) } } func TestEncoderInt32(t *testing.T) { var testCasesBasic = []struct { name string v int32 expectedJSON string }{ { name: "basic", v: int32(1), expectedJSON: "[1,1]", }, { name: "big", v: math.MaxInt32, expectedJSON: "[2147483647,2147483647]", }, { name: "big", v: int32(0), expectedJSON: "[0,0]", }, } for _, testCase := range testCasesBasic { t.Run(testCase.name, func(t *testing.T) { var b = &strings.Builder{} var enc = NewEncoder(b) enc.Encode(EncodeArrayFunc(func(enc *Encoder) { enc.Int32(testCase.v) enc.AddInt32(testCase.v) })) assert.Equal(t, testCase.expectedJSON, b.String()) }) } var testCasesOmitEmpty = []struct { name string v int32 expectedJSON string }{ { name: "basic", v: int32(1), expectedJSON: "[1,1]", }, { name: "big", v: math.MaxInt32, expectedJSON: "[2147483647,2147483647]", }, { name: "big", v: int32(0), expectedJSON: "[]", }, } for _, testCase := range testCasesOmitEmpty { t.Run(testCase.name, func(t *testing.T) { var b = &strings.Builder{} var enc = NewEncoder(b) enc.Encode(EncodeArrayFunc(func(enc *Encoder) { enc.Int32OmitEmpty(testCase.v) enc.AddInt32OmitEmpty(testCase.v) })) assert.Equal(t, testCase.expectedJSON, b.String()) }) } var testCasesKeyBasic = []struct { name string v int32 expectedJSON string }{ { name: "basic", v: int32(1), expectedJSON: `{"foo":1,"bar":1}`, }, { name: "big", v: math.MaxInt32, expectedJSON: `{"foo":2147483647,"bar":2147483647}`, }, { name: "big", v: int32(0), expectedJSON: `{"foo":0,"bar":0}`, }, } for _, testCase := range testCasesKeyBasic { t.Run(testCase.name, func(t *testing.T) { var b = &strings.Builder{} var enc = NewEncoder(b) enc.Encode(EncodeObjectFunc(func(enc *Encoder) { enc.Int32Key("foo", testCase.v) enc.AddInt32Key("bar", testCase.v) })) assert.Equal(t, testCase.expectedJSON, b.String()) }) } var testCasesKeyOmitEmpty = []struct { name string v int32 expectedJSON string }{ { name: "basic", v: int32(1), expectedJSON: `{"foo":1,"bar":1}`, }, { name: "big", v: math.MaxInt32, expectedJSON: `{"foo":2147483647,"bar":2147483647}`, }, { name: "big", v: int32(0), expectedJSON: `{}`, }, } for _, testCase := range testCasesKeyOmitEmpty { t.Run(testCase.name, func(t *testing.T) { var b = &strings.Builder{} var enc = NewEncoder(b) enc.Encode(EncodeObjectFunc(func(enc *Encoder) { enc.Int32KeyOmitEmpty("foo", testCase.v) enc.AddInt32KeyOmitEmpty("bar", testCase.v) })) assert.Equal(t, testCase.expectedJSON, b.String()) }) } } func TestEncoderInt16(t *testing.T) { var testCasesBasic = []struct { name string v int16 expectedJSON string }{ { name: "basic", v: int16(1), expectedJSON: "[1,1]", }, { name: "big", v: math.MaxInt16, expectedJSON: "[32767,32767]", }, { name: "big", v: int16(0), expectedJSON: "[0,0]", }, } for _, testCase := range testCasesBasic { t.Run(testCase.name, func(t *testing.T) { var b = &strings.Builder{} var enc = NewEncoder(b) enc.Encode(EncodeArrayFunc(func(enc *Encoder) { enc.Int16(testCase.v) enc.AddInt16(testCase.v) })) assert.Equal(t, testCase.expectedJSON, b.String()) }) } var testCasesOmitEmpty = []struct { name string v int16 expectedJSON string }{ { name: "basic", v: int16(1), expectedJSON: "[1,1]", }, { name: "big", v: math.MaxInt16, expectedJSON: "[32767,32767]", }, { name: "big", v: int16(0), expectedJSON: "[]", }, } for _, testCase := range testCasesOmitEmpty { t.Run(testCase.name, func(t *testing.T) { var b = &strings.Builder{} var enc = NewEncoder(b) enc.Encode(EncodeArrayFunc(func(enc *Encoder) { enc.Int16OmitEmpty(testCase.v) enc.AddInt16OmitEmpty(testCase.v) })) assert.Equal(t, testCase.expectedJSON, b.String()) }) } var testCasesKeyBasic = []struct { name string v int16 expectedJSON string }{ { name: "basic", v: int16(1), expectedJSON: `{"foo":1,"bar":1}`, }, { name: "big", v: math.MaxInt16, expectedJSON: `{"foo":32767,"bar":32767}`, }, { name: "big", v: int16(0), expectedJSON: `{"foo":0,"bar":0}`, }, } for _, testCase := range testCasesKeyBasic { t.Run(testCase.name, func(t *testing.T) { var b = &strings.Builder{} var enc = NewEncoder(b) enc.Encode(EncodeObjectFunc(func(enc *Encoder) { enc.Int16Key("foo", testCase.v) enc.AddInt16Key("bar", testCase.v) })) assert.Equal(t, testCase.expectedJSON, b.String()) }) } var testCasesKeyOmitEmpty = []struct { name string v int16 expectedJSON string }{ { name: "basic", v: int16(1), expectedJSON: `{"foo":1,"bar":1}`, }, { name: "big", v: math.MaxInt16, expectedJSON: `{"foo":32767,"bar":32767}`, }, { name: "big", v: int16(0), expectedJSON: `{}`, }, } for _, testCase := range testCasesKeyOmitEmpty { t.Run(testCase.name, func(t *testing.T) { var b = &strings.Builder{} var enc = NewEncoder(b) enc.Encode(EncodeObjectFunc(func(enc *Encoder) { enc.Int16KeyOmitEmpty("foo", testCase.v) enc.AddInt16KeyOmitEmpty("bar", testCase.v) })) assert.Equal(t, testCase.expectedJSON, b.String()) }) } } func TestEncoderInt8(t *testing.T) { var testCasesBasic = []struct { name string v int8 expectedJSON string }{ { name: "basic", v: int8(1), expectedJSON: "[1,1]", }, { name: "big", v: math.MaxInt8, expectedJSON: "[127,127]", }, { name: "big", v: int8(0), expectedJSON: "[0,0]", }, } for _, testCase := range testCasesBasic { t.Run(testCase.name, func(t *testing.T) { var b = &strings.Builder{} var enc = NewEncoder(b) enc.Encode(EncodeArrayFunc(func(enc *Encoder) { enc.Int8(testCase.v) enc.AddInt8(testCase.v) })) assert.Equal(t, testCase.expectedJSON, b.String()) }) } var testCasesOmitEmpty = []struct { name string v int8 expectedJSON string }{ { name: "basic", v: int8(1), expectedJSON: "[1,1]", }, { name: "big", v: math.MaxInt8, expectedJSON: "[127,127]", }, { name: "big", v: int8(0), expectedJSON: "[]", }, } for _, testCase := range testCasesOmitEmpty { t.Run(testCase.name, func(t *testing.T) { var b = &strings.Builder{} var enc = NewEncoder(b) enc.Encode(EncodeArrayFunc(func(enc *Encoder) { enc.Int8OmitEmpty(testCase.v) enc.AddInt8OmitEmpty(testCase.v) })) assert.Equal(t, testCase.expectedJSON, b.String()) }) } var testCasesKeyBasic = []struct { name string v int8 expectedJSON string }{ { name: "basic", v: int8(1), expectedJSON: `{"foo":1,"bar":1}`, }, { name: "big", v: math.MaxInt8, expectedJSON: `{"foo":127,"bar":127}`, }, { name: "big", v: int8(0), expectedJSON: `{"foo":0,"bar":0}`, }, } for _, testCase := range testCasesKeyBasic { t.Run(testCase.name, func(t *testing.T) { var b = &strings.Builder{} var enc = NewEncoder(b) enc.Encode(EncodeObjectFunc(func(enc *Encoder) { enc.Int8Key("foo", testCase.v) enc.AddInt8Key("bar", testCase.v) })) assert.Equal(t, testCase.expectedJSON, b.String()) }) } var testCasesKeyOmitEmpty = []struct { name string v int8 expectedJSON string }{ { name: "basic", v: int8(1), expectedJSON: `{"foo":1,"bar":1}`, }, { name: "big", v: math.MaxInt8, expectedJSON: `{"foo":127,"bar":127}`, }, { name: "big", v: int8(0), expectedJSON: `{}`, }, } for _, testCase := range testCasesKeyOmitEmpty { t.Run(testCase.name, func(t *testing.T) { var b = &strings.Builder{} var enc = NewEncoder(b) enc.Encode(EncodeObjectFunc(func(enc *Encoder) { enc.Int8KeyOmitEmpty("foo", testCase.v) enc.AddInt8KeyOmitEmpty("bar", testCase.v) })) assert.Equal(t, testCase.expectedJSON, b.String()) }) } } func TestEncoderIntNullEmpty(t *testing.T) { var testCases = []struct { name string baseJSON string expectedJSON string }{ { name: "basic 1st elem", baseJSON: "[", expectedJSON: `[null,1`, }, { name: "basic 2nd elem", baseJSON: `["test"`, expectedJSON: `["test",null,1`, }, } for _, testCase := range testCases { t.Run("true", func(t *testing.T) { var b strings.Builder var enc = NewEncoder(&b) enc.writeString(testCase.baseJSON) enc.IntNullEmpty(0) enc.AddIntNullEmpty(1) enc.Write() assert.Equal(t, testCase.expectedJSON, b.String()) }) } } func TestEncoderIntKeyNullEmpty(t *testing.T) { var testCases = []struct { name string baseJSON string expectedJSON string }{ { name: "basic 1st elem", baseJSON: "{", expectedJSON: `{"foo":null,"bar":1`, }, { name: "basic 2nd elem", baseJSON: `{"test":"test"`, expectedJSON: `{"test":"test","foo":null,"bar":1`, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { var b strings.Builder var enc = NewEncoder(&b) enc.writeString(testCase.baseJSON) enc.IntKeyNullEmpty("foo", 0) enc.AddIntKeyNullEmpty("bar", 1) enc.Write() assert.Equal(t, testCase.expectedJSON, b.String()) }) } } func TestEncoderInt64NullEmpty(t *testing.T) { var testCases = []struct { name string baseJSON string expectedJSON string }{ { name: "basic 1st elem", baseJSON: "[", expectedJSON: `[null,1`, }, { name: "basic 2nd elem", baseJSON: `["test"`, expectedJSON: `["test",null,1`, }, } for _, testCase := range testCases { t.Run("true", func(t *testing.T) { var b strings.Builder var enc = NewEncoder(&b) enc.writeString(testCase.baseJSON) enc.Int64NullEmpty(0) enc.AddInt64NullEmpty(1) enc.Write() assert.Equal(t, testCase.expectedJSON, b.String()) }) } } func TestEncoderInt64KeyNullEmpty(t *testing.T) { var testCases = []struct { name string baseJSON string expectedJSON string }{ { name: "basic 1st elem", baseJSON: "{", expectedJSON: `{"foo":null,"bar":1`, }, { name: "basic 2nd elem", baseJSON: `{"test":"test"`, expectedJSON: `{"test":"test","foo":null,"bar":1`, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { var b strings.Builder var enc = NewEncoder(&b) enc.writeString(testCase.baseJSON) enc.Int64KeyNullEmpty("foo", 0) enc.AddInt64KeyNullEmpty("bar", 1) enc.Write() assert.Equal(t, testCase.expectedJSON, b.String()) }) } } func TestEncoderInt32NullEmpty(t *testing.T) { var testCases = []struct { name string baseJSON string expectedJSON string }{ { name: "basic 1st elem", baseJSON: "[", expectedJSON: `[null,1`, }, { name: "basic 2nd elem", baseJSON: `["test"`, expectedJSON: `["test",null,1`, }, } for _, testCase := range testCases { t.Run("true", func(t *testing.T) { var b strings.Builder var enc = NewEncoder(&b) enc.writeString(testCase.baseJSON) enc.Int32NullEmpty(0) enc.AddInt32NullEmpty(1) enc.Write() assert.Equal(t, testCase.expectedJSON, b.String()) }) } } func TestEncoderInt32KeyNullEmpty(t *testing.T) { var testCases = []struct { name string baseJSON string expectedJSON string }{ { name: "basic 1st elem", baseJSON: "{", expectedJSON: `{"foo":null,"bar":1`, }, { name: "basic 2nd elem", baseJSON: `{"test":"test"`, expectedJSON: `{"test":"test","foo":null,"bar":1`, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { var b strings.Builder var enc = NewEncoder(&b) enc.writeString(testCase.baseJSON) enc.Int32KeyNullEmpty("foo", 0) enc.Int32KeyNullEmpty("bar", int32(1)) enc.Write() assert.Equal(t, testCase.expectedJSON, b.String()) }) } } func TestEncoderInt16NullEmpty(t *testing.T) { var testCases = []struct { name string baseJSON string expectedJSON string }{ { name: "basic 1st elem", baseJSON: "[", expectedJSON: `[null,1`, }, { name: "basic 2nd elem", baseJSON: `["test"`, expectedJSON: `["test",null,1`, }, } for _, testCase := range testCases { t.Run("true", func(t *testing.T) { var b strings.Builder var enc = NewEncoder(&b) enc.writeString(testCase.baseJSON) enc.Int16NullEmpty(0) enc.Int16NullEmpty(1) enc.Write() assert.Equal(t, testCase.expectedJSON, b.String()) }) } } func TestEncoderInt16KeyNullEmpty(t *testing.T) { var testCases = []struct { name string baseJSON string expectedJSON string }{ { name: "basic 1st elem", baseJSON: "{", expectedJSON: `{"foo":null,"bar":1`, }, { name: "basic 2nd elem", baseJSON: `{"test":"test"`, expectedJSON: `{"test":"test","foo":null,"bar":1`, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { var b strings.Builder var enc = NewEncoder(&b) enc.writeString(testCase.baseJSON) enc.AddInt16KeyNullEmpty("foo", 0) enc.Int16KeyNullEmpty("bar", int16(1)) enc.Write() assert.Equal(t, testCase.expectedJSON, b.String()) }) } } func TestEncoderInt8NullEmpty(t *testing.T) { var testCases = []struct { name string baseJSON string expectedJSON string }{ { name: "basic 1st elem", baseJSON: "[", expectedJSON: `[null,1`, }, { name: "basic 2nd elem", baseJSON: `["test"`, expectedJSON: `["test",null,1`, }, } for _, testCase := range testCases { t.Run("true", func(t *testing.T) { var b strings.Builder var enc = NewEncoder(&b) enc.writeString(testCase.baseJSON) enc.AddInt8NullEmpty(0) enc.Int8NullEmpty(1) enc.Write() assert.Equal(t, testCase.expectedJSON, b.String()) }) } } func TestEncoderInt8KeyNullEmpty(t *testing.T) { var testCases = []struct { name string baseJSON string expectedJSON string }{ { name: "basic 1st elem", baseJSON: "{", expectedJSON: `{"foo":null,"bar":1`, }, { name: "basic 2nd elem", baseJSON: `{"test":"test"`, expectedJSON: `{"test":"test","foo":null,"bar":1`, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { var b strings.Builder var enc = NewEncoder(&b) enc.writeString(testCase.baseJSON) enc.AddInt8KeyNullEmpty("foo", 0) enc.Int8KeyNullEmpty("bar", int8(1)) enc.Write() assert.Equal(t, testCase.expectedJSON, b.String()) }) } } golang-github-francoispqt-gojay-1.2.13/encode_number_test.go000066400000000000000000000350341365331665100241740ustar00rootroot00000000000000package gojay import ( "strings" "testing" "fmt" "math" "github.com/stretchr/testify/assert" ) func TestEncoderNumberEncodeAPI(t *testing.T) { t.Run("encoder-int", func(t *testing.T) { builder := &strings.Builder{} enc := NewEncoder(builder) err := enc.EncodeInt(1) assert.Nil(t, err, "Error should be nil") assert.Equal( t, `1`, builder.String(), "Result of marshalling is different as the one expected") }) t.Run("encode-int64", func(t *testing.T) { builder := &strings.Builder{} enc := NewEncoder(builder) err := enc.EncodeInt64(math.MaxInt64) assert.Nil(t, err, "Error should be nil") assert.Equal( t, fmt.Sprintf("%d", math.MaxInt64), builder.String(), "Result of marshalling is different as the one expected") }) t.Run("encode-uint64", func(t *testing.T) { builder := &strings.Builder{} enc := NewEncoder(builder) err := enc.EncodeUint64(uint64(math.MaxUint64)) assert.Nil(t, err, "Error should be nil") assert.Equal( t, fmt.Sprintf("%d", uint64(math.MaxUint64)), builder.String(), "Result of marshalling is different as the one expected") }) t.Run("encode-float64", func(t *testing.T) { builder := &strings.Builder{} enc := NewEncoder(builder) err := enc.EncodeFloat(float64(1.1)) assert.Nil(t, err, "Error should be nil") assert.Equal( t, `1.1`, builder.String(), "Result of marshalling is different as the one expected") }) t.Run("encode-float32", func(t *testing.T) { builder := &strings.Builder{} enc := NewEncoder(builder) err := enc.EncodeFloat32(float32(1.12)) assert.Nil(t, err, "Error should be nil") assert.Equal( t, `1.12`, builder.String(), "Result of marshalling is different as the one expected") }) } func TestEncoderNumberEncodeAPIErrors(t *testing.T) { t.Run("encode-int-pool-error", func(t *testing.T) { builder := &strings.Builder{} enc := NewEncoder(builder) enc.isPooled = 1 defer func() { err := recover() assert.NotNil(t, err, "err should not be nil") assert.IsType(t, InvalidUsagePooledEncoderError(""), err, "err should be of type InvalidUsagePooledEncoderError") }() _ = enc.EncodeInt(1) assert.True(t, false, "should not be called as encoder should have panicked") }) t.Run("encode-int-write-error", func(t *testing.T) { w := TestWriterError("") enc := NewEncoder(w) err := enc.EncodeInt(1) assert.NotNil(t, err, "err should not be nil") assert.Equal(t, "Test Error", err.Error(), "err should be of type InvalidUsagePooledEncoderError") }) t.Run("encode-int64-pool-error", func(t *testing.T) { builder := &strings.Builder{} enc := NewEncoder(builder) enc.isPooled = 1 defer func() { err := recover() assert.NotNil(t, err, "err should not be nil") assert.IsType(t, InvalidUsagePooledEncoderError(""), err, "err should be of type InvalidUsagePooledEncoderError") }() _ = enc.EncodeInt64(1) assert.True(t, false, "should not be called as encoder should have panicked") }) t.Run("encode-int64-write-error", func(t *testing.T) { w := TestWriterError("") enc := NewEncoder(w) err := enc.EncodeInt64(1) assert.NotNil(t, err, "err should not be nil") assert.Equal(t, "Test Error", err.Error(), "err should be of type InvalidUsagePooledEncoderError") }) t.Run("encode-uint64-pool-error", func(t *testing.T) { builder := &strings.Builder{} enc := NewEncoder(builder) enc.isPooled = 1 defer func() { err := recover() assert.NotNil(t, err, "err should not be nil") assert.IsType(t, InvalidUsagePooledEncoderError(""), err, "err should be of type InvalidUsagePooledEncoderError") }() _ = enc.EncodeUint64(1) assert.True(t, false, "should not be called as encoder should have panicked") }) t.Run("encode-unt64-write-error", func(t *testing.T) { w := TestWriterError("") enc := NewEncoder(w) err := enc.EncodeUint64(1) assert.NotNil(t, err, "err should not be nil") assert.Equal(t, "Test Error", err.Error(), "err should be of type InvalidUsagePooledEncoderError") }) t.Run("encode-float64-pool-error", func(t *testing.T) { builder := &strings.Builder{} enc := NewEncoder(builder) enc.isPooled = 1 defer func() { err := recover() assert.NotNil(t, err, "err should not be nil") assert.IsType(t, InvalidUsagePooledEncoderError(""), err, "err should be of type InvalidUsagePooledEncoderError") }() _ = enc.EncodeFloat(1.1) assert.True(t, false, "should not be called as encoder should have panicked") }) t.Run("encode-float64-write-error", func(t *testing.T) { w := TestWriterError("") enc := NewEncoder(w) err := enc.EncodeFloat(1.1) assert.NotNil(t, err, "err should not be nil") assert.Equal(t, "Test Error", err.Error(), "err should be of type InvalidUsagePooledEncoderError") }) t.Run("encode-float32-pool-error", func(t *testing.T) { builder := &strings.Builder{} enc := NewEncoder(builder) enc.isPooled = 1 defer func() { err := recover() assert.NotNil(t, err, "err should not be nil") assert.IsType(t, InvalidUsagePooledEncoderError(""), err, "err should be of type InvalidUsagePooledEncoderError") }() _ = enc.EncodeFloat32(float32(1.1)) assert.True(t, false, "should not be called as encoder should have panicked") }) t.Run("encode-float32-write-error", func(t *testing.T) { w := TestWriterError("") enc := NewEncoder(w) err := enc.EncodeFloat32(float32(1.1)) assert.NotNil(t, err, "err should not be nil") assert.Equal(t, "Test Error", err.Error(), "err should be of type InvalidUsagePooledEncoderError") }) } func TestEncoderNumberMarshalAPI(t *testing.T) { t.Run("int", func(t *testing.T) { r, err := Marshal(1) assert.Nil(t, err, "Error should be nil") assert.Equal( t, `1`, string(r), "Result of marshalling is different as the one expected") }) t.Run("int64", func(t *testing.T) { r, err := Marshal(int64(1)) assert.Nil(t, err, "Error should be nil") assert.Equal( t, `1`, string(r), "Result of marshalling is different as the one expected") }) t.Run("int32", func(t *testing.T) { r, err := Marshal(int32(1)) assert.Nil(t, err, "Error should be nil") assert.Equal( t, `1`, string(r), "Result of marshalling is different as the one expected") }) t.Run("int16", func(t *testing.T) { r, err := Marshal(int16(1)) assert.Nil(t, err, "Error should be nil") assert.Equal( t, `1`, string(r), "Result of marshalling is different as the one expected") }) t.Run("int8", func(t *testing.T) { r, err := Marshal(int8(1)) assert.Nil(t, err, "Error should be nil") assert.Equal( t, `1`, string(r), "Result of marshalling is different as the one expected") }) t.Run("uint64", func(t *testing.T) { r, err := Marshal(uint64(1)) assert.Nil(t, err, "Error should be nil") assert.Equal( t, `1`, string(r), "Result of marshalling is different as the one expected") }) t.Run("uint32", func(t *testing.T) { r, err := Marshal(uint32(1)) assert.Nil(t, err, "Error should be nil") assert.Equal( t, `1`, string(r), "Result of marshalling is different as the one expected") }) t.Run("uint16", func(t *testing.T) { r, err := Marshal(uint16(1)) assert.Nil(t, err, "Error should be nil") assert.Equal( t, `1`, string(r), "Result of marshalling is different as the one expected") }) t.Run("uint8", func(t *testing.T) { r, err := Marshal(uint8(1)) assert.Nil(t, err, "Error should be nil") assert.Equal( t, `1`, string(r), "Result of marshalling is different as the one expected") }) t.Run("float64", func(t *testing.T) { r, err := Marshal(1.1) assert.Nil(t, err, "Error should be nil") assert.Equal( t, `1.1`, string(r), "Result of marshalling is different as the one expected") }) } func TestAddNumberFunc(t *testing.T) { t.Run("int64-key", func(t *testing.T) { builder := &strings.Builder{} enc := BorrowEncoder(builder) enc.writeByte('{') enc.AddInt64Key("test", 10) _, err := enc.Write() assert.Nil(t, err, "err should be nil") assert.Equal(t, `{"test":10`, builder.String(), `builder.String() should be equal to {"test":10"`) }) t.Run("int64-key-2", func(t *testing.T) { builder := &strings.Builder{} enc := BorrowEncoder(builder) enc.writeBytes([]byte(`{"test":1`)) enc.AddInt64Key("test", 10) _, err := enc.Write() assert.Nil(t, err, "err should be nil") assert.Equal(t, `{"test":1,"test":10`, builder.String(), `builder.String() should be equal to {"test":10"`) }) t.Run("int64-key-omit-empty", func(t *testing.T) { builder := &strings.Builder{} enc := BorrowEncoder(builder) enc.writeByte('{') enc.AddInt64KeyOmitEmpty("test", 10) _, err := enc.Write() assert.Nil(t, err, "err should be nil") assert.Equal(t, `{"test":10`, builder.String(), `builder.String() should be equal to {"test":10"`) }) t.Run("int64-key-omit-empty-2", func(t *testing.T) { builder := &strings.Builder{} enc := BorrowEncoder(builder) enc.writeBytes([]byte(`{"test":1`)) enc.AddInt64KeyOmitEmpty("test", 10) _, err := enc.Write() assert.Nil(t, err, "err should be nil") assert.Equal(t, `{"test":1,"test":10`, builder.String(), `builder.String() should be equal to {"test":10"`) }) t.Run("int64-key-omit-empty-3", func(t *testing.T) { builder := &strings.Builder{} enc := BorrowEncoder(builder) enc.writeByte('{') enc.AddInt64KeyOmitEmpty("test", 0) _, err := enc.Write() assert.Nil(t, err, "err should be nil") assert.Equal(t, `{`, builder.String(), `builder.String() should be equal to {"test":10"`) }) t.Run("int64", func(t *testing.T) { builder := &strings.Builder{} enc := BorrowEncoder(builder) enc.writeByte('[') enc.AddInt64(10) _, err := enc.Write() assert.Nil(t, err, "err should be nil") assert.Equal(t, `[10`, builder.String(), `builder.String() should be equal to {"test":10"`) }) t.Run("int64-2", func(t *testing.T) { builder := &strings.Builder{} enc := BorrowEncoder(builder) enc.writeBytes([]byte(`[1`)) enc.AddInt64(10) _, err := enc.Write() assert.Nil(t, err, "err should be nil") assert.Equal(t, `[1,10`, builder.String(), `builder.String() should be equal to {"test":10"`) }) t.Run("int64-omit-empty", func(t *testing.T) { builder := &strings.Builder{} enc := BorrowEncoder(builder) enc.writeByte('[') enc.AddInt64OmitEmpty(10) _, err := enc.Write() assert.Nil(t, err, "err should be nil") assert.Equal(t, `[10`, builder.String(), `builder.String() should be equal to {"test":10"`) }) t.Run("int64-omit-empty-2", func(t *testing.T) { builder := &strings.Builder{} enc := BorrowEncoder(builder) enc.writeBytes([]byte(`[1`)) enc.AddInt64OmitEmpty(10) _, err := enc.Write() assert.Nil(t, err, "err should be nil") assert.Equal(t, `[1,10`, builder.String(), `builder.String() should be equal to {"test":10"`) }) t.Run("int64-omit-empty-3", func(t *testing.T) { builder := &strings.Builder{} enc := BorrowEncoder(builder) enc.writeByte('[') enc.AddInt64OmitEmpty(0) _, err := enc.Write() assert.Nil(t, err, "err should be nil") assert.Equal(t, `[`, builder.String(), `builder.String() should be equal to {"test":10"`) }) } func TestEncodeUint64(t *testing.T) { builder := &strings.Builder{} enc := BorrowEncoder(builder) err := enc.Encode(uint64(145509)) assert.Nil(t, err, "err should be nil") assert.Equal(t, "145509", builder.String(), "builder.String() should be 145509") } func TestUint64Add(t *testing.T) { t.Run("uint64-key", func(t *testing.T) { builder := &strings.Builder{} enc := BorrowEncoder(builder) enc.writeByte('{') enc.AddUint64Key("test", 10) _, err := enc.Write() assert.Nil(t, err, "err should be nil") assert.Equal(t, `{"test":10`, builder.String(), `builder.String() should be equal to {"test":10"`) }) t.Run("uint64-key-2", func(t *testing.T) { builder := &strings.Builder{} enc := BorrowEncoder(builder) enc.writeBytes([]byte(`{"test":1`)) enc.AddUint64Key("test", 10) _, err := enc.Write() assert.Nil(t, err, "err should be nil") assert.Equal(t, `{"test":1,"test":10`, builder.String(), `builder.String() should be equal to {"test":10"`) }) t.Run("uint64-key-omit-empty", func(t *testing.T) { builder := &strings.Builder{} enc := BorrowEncoder(builder) enc.writeByte('{') enc.AddUint64KeyOmitEmpty("test", 10) _, err := enc.Write() assert.Nil(t, err, "err should be nil") assert.Equal(t, `{"test":10`, builder.String(), `builder.String() should be equal to {"test":10"`) }) t.Run("uint64-key-omit-empty-2", func(t *testing.T) { builder := &strings.Builder{} enc := BorrowEncoder(builder) enc.writeBytes([]byte(`{"test":1`)) enc.AddUint64KeyOmitEmpty("test", 10) _, err := enc.Write() assert.Nil(t, err, "err should be nil") assert.Equal(t, `{"test":1,"test":10`, builder.String(), `builder.String() should be equal to {"test":10"`) }) t.Run("uint64-key-omit-empty-3", func(t *testing.T) { builder := &strings.Builder{} enc := BorrowEncoder(builder) enc.writeByte('{') enc.AddUint64KeyOmitEmpty("test", 0) _, err := enc.Write() assert.Nil(t, err, "err should be nil") assert.Equal(t, `{`, builder.String(), `builder.String() should be equal to {"test":10"`) }) t.Run("uint64", func(t *testing.T) { builder := &strings.Builder{} enc := BorrowEncoder(builder) enc.writeByte('[') enc.AddUint64(10) _, err := enc.Write() assert.Nil(t, err, "err should be nil") assert.Equal(t, `[10`, builder.String(), `builder.String() should be equal to {"test":10"`) }) t.Run("uint64-2", func(t *testing.T) { builder := &strings.Builder{} enc := BorrowEncoder(builder) enc.writeBytes([]byte(`[1`)) enc.AddUint64(10) _, err := enc.Write() assert.Nil(t, err, "err should be nil") assert.Equal(t, `[1,10`, builder.String(), `builder.String() should be equal to {"test":10"`) }) t.Run("uint64-omit-empty", func(t *testing.T) { builder := &strings.Builder{} enc := BorrowEncoder(builder) enc.writeByte('[') enc.AddUint64OmitEmpty(10) _, err := enc.Write() assert.Nil(t, err, "err should be nil") assert.Equal(t, `[10`, builder.String(), `builder.String() should be equal to {"test":10"`) }) t.Run("uint64-omit-empty-2", func(t *testing.T) { builder := &strings.Builder{} enc := BorrowEncoder(builder) enc.writeBytes([]byte(`[1`)) enc.AddUint64OmitEmpty(10) _, err := enc.Write() assert.Nil(t, err, "err should be nil") assert.Equal(t, `[1,10`, builder.String(), `builder.String() should be equal to {"test":10"`) }) t.Run("uint64-omit-empty-3", func(t *testing.T) { builder := &strings.Builder{} enc := BorrowEncoder(builder) enc.writeByte('[') enc.AddUint64OmitEmpty(0) _, err := enc.Write() assert.Nil(t, err, "err should be nil") assert.Equal(t, `[`, builder.String(), `builder.String() should be equal to {"test":10"`) }) } golang-github-francoispqt-gojay-1.2.13/encode_number_uint.go000066400000000000000000000276661365331665100242100ustar00rootroot00000000000000package gojay import "strconv" // EncodeUint64 encodes an int64 to JSON func (enc *Encoder) EncodeUint64(n uint64) error { if enc.isPooled == 1 { panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder")) } _, _ = enc.encodeUint64(n) _, err := enc.Write() if err != nil { return err } return nil } // encodeUint64 encodes an int to JSON func (enc *Encoder) encodeUint64(n uint64) ([]byte, error) { enc.buf = strconv.AppendUint(enc.buf, n, 10) return enc.buf, nil } // AddUint64 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key) func (enc *Encoder) AddUint64(v uint64) { enc.Uint64(v) } // AddUint64OmitEmpty adds an int to be encoded and skips it if its value is 0, // must be used inside a slice or array encoding (does not encode a key). func (enc *Encoder) AddUint64OmitEmpty(v uint64) { enc.Uint64OmitEmpty(v) } // AddUint64NullEmpty adds an int to be encoded and skips it if its value is 0, // must be used inside a slice or array encoding (does not encode a key). func (enc *Encoder) AddUint64NullEmpty(v uint64) { enc.Uint64NullEmpty(v) } // Uint64 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key) func (enc *Encoder) Uint64(v uint64) { enc.grow(10) r := enc.getPreviousRune() if r != '[' { enc.writeByte(',') } enc.buf = strconv.AppendUint(enc.buf, v, 10) } // Uint64OmitEmpty adds an int to be encoded and skips it if its value is 0, // must be used inside a slice or array encoding (does not encode a key). func (enc *Encoder) Uint64OmitEmpty(v uint64) { if v == 0 { return } enc.grow(10) r := enc.getPreviousRune() if r != '[' { enc.writeByte(',') } enc.buf = strconv.AppendUint(enc.buf, v, 10) } // Uint64NullEmpty adds an int to be encoded and skips it if its value is 0, // must be used inside a slice or array encoding (does not encode a key). func (enc *Encoder) Uint64NullEmpty(v uint64) { enc.grow(10) r := enc.getPreviousRune() if r != '[' { enc.writeByte(',') } if v == 0 { enc.writeBytes(nullBytes) return } enc.buf = strconv.AppendUint(enc.buf, v, 10) } // AddUint64Key adds an int to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) AddUint64Key(key string, v uint64) { enc.Uint64Key(key, v) } // AddUint64KeyOmitEmpty adds an int to be encoded and skips it if its value is 0. // Must be used inside an object as it will encode a key. func (enc *Encoder) AddUint64KeyOmitEmpty(key string, v uint64) { enc.Uint64KeyOmitEmpty(key, v) } // AddUint64KeyNullEmpty adds an int to be encoded and skips it if its value is 0. // Must be used inside an object as it will encode a key. func (enc *Encoder) AddUint64KeyNullEmpty(key string, v uint64) { enc.Uint64KeyNullEmpty(key, v) } // Uint64Key adds an int to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) Uint64Key(key string, v uint64) { if enc.hasKeys { if !enc.keyExists(key) { return } } enc.grow(10 + len(key)) r := enc.getPreviousRune() if r != '{' { enc.writeByte(',') } enc.writeByte('"') enc.writeStringEscape(key) enc.writeBytes(objKey) enc.buf = strconv.AppendUint(enc.buf, v, 10) } // Uint64KeyOmitEmpty adds an int to be encoded and skips it if its value is 0. // Must be used inside an object as it will encode a key. func (enc *Encoder) Uint64KeyOmitEmpty(key string, v uint64) { if enc.hasKeys { if !enc.keyExists(key) { return } } if v == 0 { return } enc.grow(10 + len(key)) r := enc.getPreviousRune() if r != '{' && r != '[' { enc.writeByte(',') } enc.writeByte('"') enc.writeStringEscape(key) enc.writeBytes(objKey) enc.buf = strconv.AppendUint(enc.buf, v, 10) } // Uint64KeyNullEmpty adds an int to be encoded and skips it if its value is 0. // Must be used inside an object as it will encode a key. func (enc *Encoder) Uint64KeyNullEmpty(key string, v uint64) { if enc.hasKeys { if !enc.keyExists(key) { return } } enc.grow(10 + len(key)) r := enc.getPreviousRune() if r != '{' && r != '[' { enc.writeByte(',') } enc.writeByte('"') enc.writeStringEscape(key) enc.writeBytes(objKey) if v == 0 { enc.writeBytes(nullBytes) return } enc.buf = strconv.AppendUint(enc.buf, v, 10) } // AddUint32 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key) func (enc *Encoder) AddUint32(v uint32) { enc.Uint64(uint64(v)) } // AddUint32OmitEmpty adds an int to be encoded and skips it if its value is 0, // must be used inside a slice or array encoding (does not encode a key). func (enc *Encoder) AddUint32OmitEmpty(v uint32) { enc.Uint64OmitEmpty(uint64(v)) } // AddUint32NullEmpty adds an int to be encoded and skips it if its value is 0, // must be used inside a slice or array encoding (does not encode a key). func (enc *Encoder) AddUint32NullEmpty(v uint32) { enc.Uint64NullEmpty(uint64(v)) } // Uint32 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key) func (enc *Encoder) Uint32(v uint32) { enc.Uint64(uint64(v)) } // Uint32OmitEmpty adds an int to be encoded and skips it if its value is 0, // must be used inside a slice or array encoding (does not encode a key). func (enc *Encoder) Uint32OmitEmpty(v uint32) { enc.Uint64OmitEmpty(uint64(v)) } // Uint32NullEmpty adds an int to be encoded and skips it if its value is 0, // must be used inside a slice or array encoding (does not encode a key). func (enc *Encoder) Uint32NullEmpty(v uint32) { enc.Uint64NullEmpty(uint64(v)) } // AddUint32Key adds an int to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) AddUint32Key(key string, v uint32) { enc.Uint64Key(key, uint64(v)) } // AddUint32KeyOmitEmpty adds an int to be encoded and skips it if its value is 0. // Must be used inside an object as it will encode a key. func (enc *Encoder) AddUint32KeyOmitEmpty(key string, v uint32) { enc.Uint64KeyOmitEmpty(key, uint64(v)) } // AddUint32KeyNullEmpty adds an int to be encoded and skips it if its value is 0. // Must be used inside an object as it will encode a key. func (enc *Encoder) AddUint32KeyNullEmpty(key string, v uint32) { enc.Uint64KeyNullEmpty(key, uint64(v)) } // Uint32Key adds an int to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) Uint32Key(key string, v uint32) { enc.Uint64Key(key, uint64(v)) } // Uint32KeyOmitEmpty adds an int to be encoded and skips it if its value is 0. // Must be used inside an object as it will encode a key. func (enc *Encoder) Uint32KeyOmitEmpty(key string, v uint32) { enc.Uint64KeyOmitEmpty(key, uint64(v)) } // Uint32KeyNullEmpty adds an int to be encoded and skips it if its value is 0. // Must be used inside an object as it will encode a key. func (enc *Encoder) Uint32KeyNullEmpty(key string, v uint32) { enc.Uint64KeyNullEmpty(key, uint64(v)) } // AddUint16 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key) func (enc *Encoder) AddUint16(v uint16) { enc.Uint64(uint64(v)) } // AddUint16OmitEmpty adds an int to be encoded and skips it if its value is 0, // must be used inside a slice or array encoding (does not encode a key). func (enc *Encoder) AddUint16OmitEmpty(v uint16) { enc.Uint64OmitEmpty(uint64(v)) } // AddUint16NullEmpty adds an int to be encoded and skips it if its value is 0, // must be used inside a slice or array encoding (does not encode a key). func (enc *Encoder) AddUint16NullEmpty(v uint16) { enc.Uint64NullEmpty(uint64(v)) } // Uint16 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key) func (enc *Encoder) Uint16(v uint16) { enc.Uint64(uint64(v)) } // Uint16OmitEmpty adds an int to be encoded and skips it if its value is 0, // must be used inside a slice or array encoding (does not encode a key). func (enc *Encoder) Uint16OmitEmpty(v uint16) { enc.Uint64OmitEmpty(uint64(v)) } // Uint16NullEmpty adds an int to be encoded and skips it if its value is 0, // must be used inside a slice or array encoding (does not encode a key). func (enc *Encoder) Uint16NullEmpty(v uint16) { enc.Uint64NullEmpty(uint64(v)) } // AddUint16Key adds an int to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) AddUint16Key(key string, v uint16) { enc.Uint64Key(key, uint64(v)) } // AddUint16KeyOmitEmpty adds an int to be encoded and skips it if its value is 0. // Must be used inside an object as it will encode a key. func (enc *Encoder) AddUint16KeyOmitEmpty(key string, v uint16) { enc.Uint64KeyOmitEmpty(key, uint64(v)) } // AddUint16KeyNullEmpty adds an int to be encoded and skips it if its value is 0. // Must be used inside an object as it will encode a key. func (enc *Encoder) AddUint16KeyNullEmpty(key string, v uint16) { enc.Uint64KeyNullEmpty(key, uint64(v)) } // Uint16Key adds an int to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) Uint16Key(key string, v uint16) { enc.Uint64Key(key, uint64(v)) } // Uint16KeyOmitEmpty adds an int to be encoded and skips it if its value is 0. // Must be used inside an object as it will encode a key. func (enc *Encoder) Uint16KeyOmitEmpty(key string, v uint16) { enc.Uint64KeyOmitEmpty(key, uint64(v)) } // Uint16KeyNullEmpty adds an int to be encoded and skips it if its value is 0. // Must be used inside an object as it will encode a key. func (enc *Encoder) Uint16KeyNullEmpty(key string, v uint16) { enc.Uint64KeyNullEmpty(key, uint64(v)) } // AddUint8 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key) func (enc *Encoder) AddUint8(v uint8) { enc.Uint64(uint64(v)) } // AddUint8OmitEmpty adds an int to be encoded and skips it if its value is 0, // must be used inside a slice or array encoding (does not encode a key). func (enc *Encoder) AddUint8OmitEmpty(v uint8) { enc.Uint64OmitEmpty(uint64(v)) } // AddUint8NullEmpty adds an int to be encoded and skips it if its value is 0, // must be used inside a slice or array encoding (does not encode a key). func (enc *Encoder) AddUint8NullEmpty(v uint8) { enc.Uint64NullEmpty(uint64(v)) } // Uint8 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key) func (enc *Encoder) Uint8(v uint8) { enc.Uint64(uint64(v)) } // Uint8OmitEmpty adds an int to be encoded and skips it if its value is 0, // must be used inside a slice or array encoding (does not encode a key). func (enc *Encoder) Uint8OmitEmpty(v uint8) { enc.Uint64OmitEmpty(uint64(v)) } // Uint8NullEmpty adds an int to be encoded and skips it if its value is 0, // must be used inside a slice or array encoding (does not encode a key). func (enc *Encoder) Uint8NullEmpty(v uint8) { enc.Uint64NullEmpty(uint64(v)) } // AddUint8Key adds an int to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) AddUint8Key(key string, v uint8) { enc.Uint64Key(key, uint64(v)) } // AddUint8KeyOmitEmpty adds an int to be encoded and skips it if its value is 0. // Must be used inside an object as it will encode a key. func (enc *Encoder) AddUint8KeyOmitEmpty(key string, v uint8) { enc.Uint64KeyOmitEmpty(key, uint64(v)) } // AddUint8KeyNullEmpty adds an int to be encoded and skips it if its value is 0. // Must be used inside an object as it will encode a key. func (enc *Encoder) AddUint8KeyNullEmpty(key string, v uint8) { enc.Uint64KeyNullEmpty(key, uint64(v)) } // Uint8Key adds an int to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) Uint8Key(key string, v uint8) { enc.Uint64Key(key, uint64(v)) } // Uint8KeyOmitEmpty adds an int to be encoded and skips it if its value is 0. // Must be used inside an object as it will encode a key. func (enc *Encoder) Uint8KeyOmitEmpty(key string, v uint8) { enc.Uint64KeyOmitEmpty(key, uint64(v)) } // Uint8KeyNullEmpty adds an int to be encoded and skips it if its value is 0. // Must be used inside an object as it will encode a key. func (enc *Encoder) Uint8KeyNullEmpty(key string, v uint8) { enc.Uint64KeyNullEmpty(key, uint64(v)) } golang-github-francoispqt-gojay-1.2.13/encode_number_uint_test.go000066400000000000000000000425471365331665100252420ustar00rootroot00000000000000package gojay import ( "math" "strings" "testing" "github.com/stretchr/testify/assert" ) func TestEncoderUint64(t *testing.T) { var testCasesBasic = []struct { name string v uint64 expectedJSON string }{ { name: "basic", v: uint64(1), expectedJSON: "[1,1]", }, { name: "big", v: math.MaxUint64, expectedJSON: "[18446744073709551615,18446744073709551615]", }, { name: "big", v: uint64(0), expectedJSON: "[0,0]", }, } for _, testCase := range testCasesBasic { t.Run(testCase.name, func(t *testing.T) { var b = &strings.Builder{} var enc = NewEncoder(b) enc.Encode(EncodeArrayFunc(func(enc *Encoder) { enc.Uint64(testCase.v) enc.AddUint64(testCase.v) })) assert.Equal(t, testCase.expectedJSON, b.String()) }) } var testCasesOmitEmpty = []struct { name string v uint64 expectedJSON string }{ { name: "basic", v: uint64(1), expectedJSON: "[1,1]", }, { name: "big", v: math.MaxUint64, expectedJSON: "[18446744073709551615,18446744073709551615]", }, { name: "big", v: uint64(0), expectedJSON: "[]", }, } for _, testCase := range testCasesOmitEmpty { t.Run(testCase.name, func(t *testing.T) { var b = &strings.Builder{} var enc = NewEncoder(b) enc.Encode(EncodeArrayFunc(func(enc *Encoder) { enc.Uint64OmitEmpty(testCase.v) enc.AddUint64OmitEmpty(testCase.v) })) assert.Equal(t, testCase.expectedJSON, b.String()) }) } var testCasesKeyBasic = []struct { name string v uint64 expectedJSON string }{ { name: "basic", v: uint64(1), expectedJSON: `{"foo":1,"bar":1}`, }, { name: "big", v: math.MaxUint64, expectedJSON: `{"foo":18446744073709551615,"bar":18446744073709551615}`, }, { name: "big", v: uint64(0), expectedJSON: `{"foo":0,"bar":0}`, }, } for _, testCase := range testCasesKeyBasic { t.Run(testCase.name, func(t *testing.T) { var b = &strings.Builder{} var enc = NewEncoder(b) enc.Encode(EncodeObjectFunc(func(enc *Encoder) { enc.Uint64Key("foo", testCase.v) enc.AddUint64Key("bar", testCase.v) })) assert.Equal(t, testCase.expectedJSON, b.String()) }) } var testCasesKeyOmitEmpty = []struct { name string v uint64 expectedJSON string }{ { name: "basic", v: uint64(1), expectedJSON: `{"foo":1,"bar":1}`, }, { name: "big", v: math.MaxUint64, expectedJSON: `{"foo":18446744073709551615,"bar":18446744073709551615}`, }, { name: "big", v: uint64(0), expectedJSON: "{}", }, } for _, testCase := range testCasesKeyOmitEmpty { t.Run(testCase.name, func(t *testing.T) { var b = &strings.Builder{} var enc = NewEncoder(b) enc.Encode(EncodeObjectFunc(func(enc *Encoder) { enc.Uint64KeyOmitEmpty("foo", testCase.v) enc.AddUint64KeyOmitEmpty("bar", testCase.v) })) assert.Equal(t, testCase.expectedJSON, b.String()) }) } } func TestEncoderUint32(t *testing.T) { var testCasesBasic = []struct { name string v uint32 expectedJSON string }{ { name: "basic", v: uint32(1), expectedJSON: "[1,1]", }, { name: "big", v: math.MaxUint32, expectedJSON: "[4294967295,4294967295]", }, { name: "big", v: uint32(0), expectedJSON: "[0,0]", }, } for _, testCase := range testCasesBasic { t.Run(testCase.name, func(t *testing.T) { var b = &strings.Builder{} var enc = NewEncoder(b) enc.Encode(EncodeArrayFunc(func(enc *Encoder) { enc.Uint32(testCase.v) enc.AddUint32(testCase.v) })) assert.Equal(t, testCase.expectedJSON, b.String()) }) } var testCasesOmitEmpty = []struct { name string v uint32 expectedJSON string }{ { name: "basic", v: uint32(1), expectedJSON: "[1,1]", }, { name: "big", v: math.MaxUint32, expectedJSON: "[4294967295,4294967295]", }, { name: "big", v: uint32(0), expectedJSON: "[]", }, } for _, testCase := range testCasesOmitEmpty { t.Run(testCase.name, func(t *testing.T) { var b = &strings.Builder{} var enc = NewEncoder(b) enc.Encode(EncodeArrayFunc(func(enc *Encoder) { enc.Uint32OmitEmpty(testCase.v) enc.AddUint32OmitEmpty(testCase.v) })) assert.Equal(t, testCase.expectedJSON, b.String()) }) } var testCasesKeyBasic = []struct { name string v uint32 expectedJSON string }{ { name: "basic", v: uint32(1), expectedJSON: `{"foo":1,"bar":1}`, }, { name: "big", v: math.MaxUint32, expectedJSON: `{"foo":4294967295,"bar":4294967295}`, }, { name: "big", v: uint32(0), expectedJSON: `{"foo":0,"bar":0}`, }, } for _, testCase := range testCasesKeyBasic { t.Run(testCase.name, func(t *testing.T) { var b = &strings.Builder{} var enc = NewEncoder(b) enc.Encode(EncodeObjectFunc(func(enc *Encoder) { enc.Uint32Key("foo", testCase.v) enc.AddUint32Key("bar", testCase.v) })) assert.Equal(t, testCase.expectedJSON, b.String()) }) } var testCasesKeyOmitEmpty = []struct { name string v uint32 expectedJSON string }{ { name: "basic", v: uint32(1), expectedJSON: `{"foo":1,"bar":1}`, }, { name: "big", v: math.MaxUint32, expectedJSON: `{"foo":4294967295,"bar":4294967295}`, }, { name: "big", v: uint32(0), expectedJSON: `{}`, }, } for _, testCase := range testCasesKeyOmitEmpty { t.Run(testCase.name, func(t *testing.T) { var b = &strings.Builder{} var enc = NewEncoder(b) enc.Encode(EncodeObjectFunc(func(enc *Encoder) { enc.Uint32KeyOmitEmpty("foo", testCase.v) enc.AddUint32KeyOmitEmpty("bar", testCase.v) })) assert.Equal(t, testCase.expectedJSON, b.String()) }) } } func TestEncoderUint16(t *testing.T) { var testCasesBasic = []struct { name string v uint16 expectedJSON string }{ { name: "basic", v: uint16(1), expectedJSON: "[1,1]", }, { name: "big", v: math.MaxUint16, expectedJSON: "[65535,65535]", }, { name: "big", v: uint16(0), expectedJSON: "[0,0]", }, } for _, testCase := range testCasesBasic { t.Run(testCase.name, func(t *testing.T) { var b = &strings.Builder{} var enc = NewEncoder(b) enc.Encode(EncodeArrayFunc(func(enc *Encoder) { enc.Uint16(testCase.v) enc.AddUint16(testCase.v) })) assert.Equal(t, testCase.expectedJSON, b.String()) }) } var testCasesOmitEmpty = []struct { name string v uint16 expectedJSON string }{ { name: "basic", v: uint16(1), expectedJSON: "[1,1]", }, { name: "big", v: math.MaxUint16, expectedJSON: "[65535,65535]", }, { name: "big", v: uint16(0), expectedJSON: "[]", }, } for _, testCase := range testCasesOmitEmpty { t.Run(testCase.name, func(t *testing.T) { var b = &strings.Builder{} var enc = NewEncoder(b) enc.Encode(EncodeArrayFunc(func(enc *Encoder) { enc.Uint16OmitEmpty(testCase.v) enc.AddUint16OmitEmpty(testCase.v) })) assert.Equal(t, testCase.expectedJSON, b.String()) }) } var testCasesKeyBasic = []struct { name string v uint16 expectedJSON string }{ { name: "basic", v: uint16(1), expectedJSON: `{"foo":1,"bar":1}`, }, { name: "big", v: math.MaxUint16, expectedJSON: `{"foo":65535,"bar":65535}`, }, { name: "big", v: uint16(0), expectedJSON: `{"foo":0,"bar":0}`, }, } for _, testCase := range testCasesKeyBasic { t.Run(testCase.name, func(t *testing.T) { var b = &strings.Builder{} var enc = NewEncoder(b) enc.Encode(EncodeObjectFunc(func(enc *Encoder) { enc.Uint16Key("foo", testCase.v) enc.AddUint16Key("bar", testCase.v) })) assert.Equal(t, testCase.expectedJSON, b.String()) }) } var testCasesKeyOmitEmpty = []struct { name string v uint16 expectedJSON string }{ { name: "basic", v: uint16(1), expectedJSON: `{"foo":1,"bar":1}`, }, { name: "big", v: math.MaxUint16, expectedJSON: `{"foo":65535,"bar":65535}`, }, { name: "big", v: uint16(0), expectedJSON: `{}`, }, } for _, testCase := range testCasesKeyOmitEmpty { t.Run(testCase.name, func(t *testing.T) { var b = &strings.Builder{} var enc = NewEncoder(b) enc.Encode(EncodeObjectFunc(func(enc *Encoder) { enc.Uint16KeyOmitEmpty("foo", testCase.v) enc.AddUint16KeyOmitEmpty("bar", testCase.v) })) assert.Equal(t, testCase.expectedJSON, b.String()) }) } } func TestEncoderUint8(t *testing.T) { var testCasesBasic = []struct { name string v uint8 expectedJSON string }{ { name: "basic", v: uint8(1), expectedJSON: "[1,1]", }, { name: "big", v: math.MaxUint8, expectedJSON: "[255,255]", }, { name: "big", v: uint8(0), expectedJSON: "[0,0]", }, } for _, testCase := range testCasesBasic { t.Run(testCase.name, func(t *testing.T) { var b = &strings.Builder{} var enc = NewEncoder(b) enc.Encode(EncodeArrayFunc(func(enc *Encoder) { enc.Uint8(testCase.v) enc.AddUint8(testCase.v) })) assert.Equal(t, testCase.expectedJSON, b.String()) }) } var testCasesOmitEmpty = []struct { name string v uint8 expectedJSON string }{ { name: "basic", v: uint8(1), expectedJSON: "[1,1]", }, { name: "big", v: math.MaxUint8, expectedJSON: "[255,255]", }, { name: "big", v: uint8(0), expectedJSON: "[]", }, } for _, testCase := range testCasesOmitEmpty { t.Run(testCase.name, func(t *testing.T) { var b = &strings.Builder{} var enc = NewEncoder(b) enc.Encode(EncodeArrayFunc(func(enc *Encoder) { enc.Uint8OmitEmpty(testCase.v) enc.AddUint8OmitEmpty(testCase.v) })) assert.Equal(t, testCase.expectedJSON, b.String()) }) } var testCasesKeyBasic = []struct { name string v uint8 expectedJSON string }{ { name: "basic", v: uint8(1), expectedJSON: `{"foo":1,"bar":1}`, }, { name: "big", v: math.MaxUint8, expectedJSON: `{"foo":255,"bar":255}`, }, { name: "big", v: uint8(0), expectedJSON: `{"foo":0,"bar":0}`, }, } for _, testCase := range testCasesKeyBasic { t.Run(testCase.name, func(t *testing.T) { var b = &strings.Builder{} var enc = NewEncoder(b) enc.Encode(EncodeObjectFunc(func(enc *Encoder) { enc.Uint8Key("foo", testCase.v) enc.AddUint8Key("bar", testCase.v) })) assert.Equal(t, testCase.expectedJSON, b.String()) }) } var testCasesKeyOmitEmpty = []struct { name string v uint8 expectedJSON string }{ { name: "basic", v: uint8(1), expectedJSON: `{"foo":1,"bar":1}`, }, { name: "big", v: math.MaxUint8, expectedJSON: `{"foo":255,"bar":255}`, }, { name: "big", v: uint8(0), expectedJSON: `{}`, }, } for _, testCase := range testCasesKeyOmitEmpty { t.Run(testCase.name, func(t *testing.T) { var b = &strings.Builder{} var enc = NewEncoder(b) enc.Encode(EncodeObjectFunc(func(enc *Encoder) { enc.Uint8KeyOmitEmpty("foo", testCase.v) enc.AddUint8KeyOmitEmpty("bar", testCase.v) })) assert.Equal(t, testCase.expectedJSON, b.String()) }) } } func TestEncoderUint64NullEmpty(t *testing.T) { var testCases = []struct { name string baseJSON string expectedJSON string }{ { name: "basic 1st elem", baseJSON: "[", expectedJSON: `[null,1`, }, { name: "basic 2nd elem", baseJSON: `["test"`, expectedJSON: `["test",null,1`, }, } for _, testCase := range testCases { t.Run("true", func(t *testing.T) { var b strings.Builder var enc = NewEncoder(&b) enc.writeString(testCase.baseJSON) enc.Uint64NullEmpty(0) enc.AddUint64NullEmpty(1) enc.Write() assert.Equal(t, testCase.expectedJSON, b.String()) }) } } func TestEncoderUint64KeyNullEmpty(t *testing.T) { var testCases = []struct { name string baseJSON string expectedJSON string }{ { name: "basic 1st elem", baseJSON: "{", expectedJSON: `{"foo":null,"bar":1`, }, { name: "basic 2nd elem", baseJSON: `{"test":"test"`, expectedJSON: `{"test":"test","foo":null,"bar":1`, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { var b strings.Builder var enc = NewEncoder(&b) enc.writeString(testCase.baseJSON) enc.Uint64KeyNullEmpty("foo", 0) enc.AddUint64KeyNullEmpty("bar", 1) enc.Write() assert.Equal(t, testCase.expectedJSON, b.String()) }) } } func TestEncoderUint32NullEmpty(t *testing.T) { var testCases = []struct { name string baseJSON string expectedJSON string }{ { name: "basic 1st elem", baseJSON: "[", expectedJSON: `[null,1`, }, { name: "basic 2nd elem", baseJSON: `["test"`, expectedJSON: `["test",null,1`, }, } for _, testCase := range testCases { t.Run("true", func(t *testing.T) { var b strings.Builder var enc = NewEncoder(&b) enc.writeString(testCase.baseJSON) enc.Uint32NullEmpty(0) enc.AddUint32NullEmpty(1) enc.Write() assert.Equal(t, testCase.expectedJSON, b.String()) }) } } func TestEncoderUint32KeyNullEmpty(t *testing.T) { var testCases = []struct { name string baseJSON string expectedJSON string }{ { name: "basic 1st elem", baseJSON: "{", expectedJSON: `{"foo":null,"bar":1`, }, { name: "basic 2nd elem", baseJSON: `{"test":"test"`, expectedJSON: `{"test":"test","foo":null,"bar":1`, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { var b strings.Builder var enc = NewEncoder(&b) enc.writeString(testCase.baseJSON) enc.AddUint32KeyNullEmpty("foo", 0) enc.Uint32KeyNullEmpty("bar", uint32(1)) enc.Write() assert.Equal(t, testCase.expectedJSON, b.String()) }) } } func TestEncoderUint16NullEmpty(t *testing.T) { var testCases = []struct { name string baseJSON string expectedJSON string }{ { name: "basic 1st elem", baseJSON: "[", expectedJSON: `[null,1`, }, { name: "basic 2nd elem", baseJSON: `["test"`, expectedJSON: `["test",null,1`, }, } for _, testCase := range testCases { t.Run("true", func(t *testing.T) { var b strings.Builder var enc = NewEncoder(&b) enc.writeString(testCase.baseJSON) enc.AddUint16NullEmpty(0) enc.Uint16NullEmpty(1) enc.Write() assert.Equal(t, testCase.expectedJSON, b.String()) }) } } func TestEncoderUint16KeyNullEmpty(t *testing.T) { var testCases = []struct { name string baseJSON string expectedJSON string }{ { name: "basic 1st elem", baseJSON: "{", expectedJSON: `{"foo":null,"bar":1`, }, { name: "basic 2nd elem", baseJSON: `{"test":"test"`, expectedJSON: `{"test":"test","foo":null,"bar":1`, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { var b strings.Builder var enc = NewEncoder(&b) enc.writeString(testCase.baseJSON) enc.AddUint16KeyNullEmpty("foo", 0) enc.Uint16KeyNullEmpty("bar", uint16(1)) enc.Write() assert.Equal(t, testCase.expectedJSON, b.String()) }) } } func TestEncoderUint8NullEmpty(t *testing.T) { var testCases = []struct { name string baseJSON string expectedJSON string }{ { name: "basic 1st elem", baseJSON: "[", expectedJSON: `[null,1`, }, { name: "basic 2nd elem", baseJSON: `["test"`, expectedJSON: `["test",null,1`, }, } for _, testCase := range testCases { t.Run("true", func(t *testing.T) { var b strings.Builder var enc = NewEncoder(&b) enc.writeString(testCase.baseJSON) enc.AddUint8NullEmpty(0) enc.Uint8NullEmpty(1) enc.Write() assert.Equal(t, testCase.expectedJSON, b.String()) }) } } func TestEncoderUint8KeyNullEmpty(t *testing.T) { var testCases = []struct { name string baseJSON string expectedJSON string }{ { name: "basic 1st elem", baseJSON: "{", expectedJSON: `{"foo":null,"bar":1`, }, { name: "basic 2nd elem", baseJSON: `{"test":"test"`, expectedJSON: `{"test":"test","foo":null,"bar":1`, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { var b strings.Builder var enc = NewEncoder(&b) enc.writeString(testCase.baseJSON) enc.AddUint8KeyNullEmpty("foo", 0) enc.Uint8KeyNullEmpty("bar", uint8(1)) enc.Write() assert.Equal(t, testCase.expectedJSON, b.String()) }) } } golang-github-francoispqt-gojay-1.2.13/encode_object.go000066400000000000000000000224431365331665100231130ustar00rootroot00000000000000package gojay var objKeyStr = []byte(`":"`) var objKeyObj = []byte(`":{`) var objKeyArr = []byte(`":[`) var objKey = []byte(`":`) // EncodeObject encodes an object to JSON func (enc *Encoder) EncodeObject(v MarshalerJSONObject) error { if enc.isPooled == 1 { panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder")) } _, err := enc.encodeObject(v) if err != nil { enc.err = err return err } _, err = enc.Write() if err != nil { enc.err = err return err } return nil } // EncodeObjectKeys encodes an object to JSON func (enc *Encoder) EncodeObjectKeys(v MarshalerJSONObject, keys []string) error { if enc.isPooled == 1 { panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder")) } enc.hasKeys = true enc.keys = keys _, err := enc.encodeObject(v) if err != nil { enc.err = err return err } _, err = enc.Write() if err != nil { enc.err = err return err } return nil } func (enc *Encoder) encodeObject(v MarshalerJSONObject) ([]byte, error) { enc.grow(512) enc.writeByte('{') if !v.IsNil() { v.MarshalJSONObject(enc) } if enc.hasKeys { enc.hasKeys = false enc.keys = nil } enc.writeByte('}') return enc.buf, enc.err } // AddObject adds an object to be encoded, must be used inside a slice or array encoding (does not encode a key) // value must implement MarshalerJSONObject func (enc *Encoder) AddObject(v MarshalerJSONObject) { enc.Object(v) } // AddObjectOmitEmpty adds an object to be encoded or skips it if IsNil returns true. // Must be used inside a slice or array encoding (does not encode a key) // value must implement MarshalerJSONObject func (enc *Encoder) AddObjectOmitEmpty(v MarshalerJSONObject) { enc.ObjectOmitEmpty(v) } // AddObjectNullEmpty adds an object to be encoded or skips it if IsNil returns true. // Must be used inside a slice or array encoding (does not encode a key) // value must implement MarshalerJSONObject func (enc *Encoder) AddObjectNullEmpty(v MarshalerJSONObject) { enc.ObjectNullEmpty(v) } // AddObjectKey adds a struct to be encoded, must be used inside an object as it will encode a key // value must implement MarshalerJSONObject func (enc *Encoder) AddObjectKey(key string, v MarshalerJSONObject) { enc.ObjectKey(key, v) } // AddObjectKeyOmitEmpty adds an object to be encoded or skips it if IsNil returns true. // Must be used inside a slice or array encoding (does not encode a key) // value must implement MarshalerJSONObject func (enc *Encoder) AddObjectKeyOmitEmpty(key string, v MarshalerJSONObject) { enc.ObjectKeyOmitEmpty(key, v) } // AddObjectKeyNullEmpty adds an object to be encoded or skips it if IsNil returns true. // Must be used inside a slice or array encoding (does not encode a key) // value must implement MarshalerJSONObject func (enc *Encoder) AddObjectKeyNullEmpty(key string, v MarshalerJSONObject) { enc.ObjectKeyNullEmpty(key, v) } // Object adds an object to be encoded, must be used inside a slice or array encoding (does not encode a key) // value must implement MarshalerJSONObject func (enc *Encoder) Object(v MarshalerJSONObject) { if v.IsNil() { enc.grow(2) r := enc.getPreviousRune() if r != '{' && r != '[' { enc.writeByte(',') } enc.writeByte('{') enc.writeByte('}') return } enc.grow(4) r := enc.getPreviousRune() if r != '[' { enc.writeByte(',') } enc.writeByte('{') var origHasKeys = enc.hasKeys var origKeys = enc.keys enc.hasKeys = false enc.keys = nil v.MarshalJSONObject(enc) enc.hasKeys = origHasKeys enc.keys = origKeys enc.writeByte('}') } // ObjectWithKeys adds an object to be encoded, must be used inside a slice or array encoding (does not encode a key) // value must implement MarshalerJSONObject. It will only encode the keys in keys. func (enc *Encoder) ObjectWithKeys(v MarshalerJSONObject, keys []string) { if v.IsNil() { enc.grow(2) r := enc.getPreviousRune() if r != '{' && r != '[' { enc.writeByte(',') } enc.writeByte('{') enc.writeByte('}') return } enc.grow(4) r := enc.getPreviousRune() if r != '[' { enc.writeByte(',') } enc.writeByte('{') var origKeys = enc.keys var origHasKeys = enc.hasKeys enc.hasKeys = true enc.keys = keys v.MarshalJSONObject(enc) enc.hasKeys = origHasKeys enc.keys = origKeys enc.writeByte('}') } // ObjectOmitEmpty adds an object to be encoded or skips it if IsNil returns true. // Must be used inside a slice or array encoding (does not encode a key) // value must implement MarshalerJSONObject func (enc *Encoder) ObjectOmitEmpty(v MarshalerJSONObject) { if v.IsNil() { return } enc.grow(2) r := enc.getPreviousRune() if r != '[' { enc.writeByte(',') } enc.writeByte('{') var origHasKeys = enc.hasKeys var origKeys = enc.keys enc.hasKeys = false enc.keys = nil v.MarshalJSONObject(enc) enc.hasKeys = origHasKeys enc.keys = origKeys enc.writeByte('}') } // ObjectNullEmpty adds an object to be encoded or skips it if IsNil returns true. // Must be used inside a slice or array encoding (does not encode a key) // value must implement MarshalerJSONObject func (enc *Encoder) ObjectNullEmpty(v MarshalerJSONObject) { enc.grow(2) r := enc.getPreviousRune() if r != '[' { enc.writeByte(',') } if v.IsNil() { enc.writeBytes(nullBytes) return } enc.writeByte('{') var origHasKeys = enc.hasKeys var origKeys = enc.keys enc.hasKeys = false enc.keys = nil v.MarshalJSONObject(enc) enc.hasKeys = origHasKeys enc.keys = origKeys enc.writeByte('}') } // ObjectKey adds a struct to be encoded, must be used inside an object as it will encode a key // value must implement MarshalerJSONObject func (enc *Encoder) ObjectKey(key string, v MarshalerJSONObject) { if enc.hasKeys { if !enc.keyExists(key) { return } } if v.IsNil() { enc.grow(2 + len(key)) r := enc.getPreviousRune() if r != '{' { enc.writeByte(',') } enc.writeByte('"') enc.writeStringEscape(key) enc.writeBytes(objKeyObj) enc.writeByte('}') return } enc.grow(5 + len(key)) r := enc.getPreviousRune() if r != '{' { enc.writeByte(',') } enc.writeByte('"') enc.writeStringEscape(key) enc.writeBytes(objKeyObj) var origHasKeys = enc.hasKeys var origKeys = enc.keys enc.hasKeys = false enc.keys = nil v.MarshalJSONObject(enc) enc.hasKeys = origHasKeys enc.keys = origKeys enc.writeByte('}') } // ObjectKeyWithKeys adds a struct to be encoded, must be used inside an object as it will encode a key. // Value must implement MarshalerJSONObject. It will only encode the keys in keys. func (enc *Encoder) ObjectKeyWithKeys(key string, value MarshalerJSONObject, keys []string) { if enc.hasKeys { if !enc.keyExists(key) { return } } if value.IsNil() { enc.grow(2 + len(key)) r := enc.getPreviousRune() if r != '{' { enc.writeByte(',') } enc.writeByte('"') enc.writeStringEscape(key) enc.writeBytes(objKeyObj) enc.writeByte('}') return } enc.grow(5 + len(key)) r := enc.getPreviousRune() if r != '{' { enc.writeByte(',') } enc.writeByte('"') enc.writeStringEscape(key) enc.writeBytes(objKeyObj) var origKeys = enc.keys var origHasKeys = enc.hasKeys enc.hasKeys = true enc.keys = keys value.MarshalJSONObject(enc) enc.hasKeys = origHasKeys enc.keys = origKeys enc.writeByte('}') } // ObjectKeyOmitEmpty adds an object to be encoded or skips it if IsNil returns true. // Must be used inside a slice or array encoding (does not encode a key) // value must implement MarshalerJSONObject func (enc *Encoder) ObjectKeyOmitEmpty(key string, v MarshalerJSONObject) { if enc.hasKeys { if !enc.keyExists(key) { return } } if v.IsNil() { return } enc.grow(5 + len(key)) r := enc.getPreviousRune() if r != '{' { enc.writeByte(',') } enc.writeByte('"') enc.writeStringEscape(key) enc.writeBytes(objKeyObj) var origHasKeys = enc.hasKeys var origKeys = enc.keys enc.hasKeys = false enc.keys = nil v.MarshalJSONObject(enc) enc.hasKeys = origHasKeys enc.keys = origKeys enc.writeByte('}') } // ObjectKeyNullEmpty adds an object to be encoded or skips it if IsNil returns true. // Must be used inside a slice or array encoding (does not encode a key) // value must implement MarshalerJSONObject func (enc *Encoder) ObjectKeyNullEmpty(key string, v MarshalerJSONObject) { if enc.hasKeys { if !enc.keyExists(key) { return } } enc.grow(5 + len(key)) r := enc.getPreviousRune() if r != '{' { enc.writeByte(',') } enc.writeByte('"') enc.writeStringEscape(key) enc.writeBytes(objKey) if v.IsNil() { enc.writeBytes(nullBytes) return } enc.writeByte('{') var origHasKeys = enc.hasKeys var origKeys = enc.keys enc.hasKeys = false enc.keys = nil v.MarshalJSONObject(enc) enc.hasKeys = origHasKeys enc.keys = origKeys enc.writeByte('}') } // EncodeObjectFunc is a custom func type implementing MarshaleObject. // Use it to cast a func(*Encoder) to Marshal an object. // // enc := gojay.NewEncoder(io.Writer) // enc.EncodeObject(gojay.EncodeObjectFunc(func(enc *gojay.Encoder) { // enc.AddStringKey("hello", "world") // })) type EncodeObjectFunc func(*Encoder) // MarshalJSONObject implements MarshalerJSONObject. func (f EncodeObjectFunc) MarshalJSONObject(enc *Encoder) { f(enc) } // IsNil implements MarshalerJSONObject. func (f EncodeObjectFunc) IsNil() bool { return f == nil } func (enc *Encoder) keyExists(k string) bool { if enc.keys == nil { return false } for _, key := range enc.keys { if key == k { return true } } return false } golang-github-francoispqt-gojay-1.2.13/encode_object_test.go000066400000000000000000000565741365331665100241660ustar00rootroot00000000000000package gojay import ( "strings" "testing" "time" "github.com/stretchr/testify/assert" ) type testObjectWithUnknownType struct { unknownType struct{} } func (t *testObjectWithUnknownType) IsNil() bool { return t == nil } func (t *testObjectWithUnknownType) MarshalJSONObject(enc *Encoder) { enc.AddInterfaceKey("unknownType", t.unknownType) } type TestEncoding struct { test string test2 string testInt int testBool bool testF32 float32 testF64 float64 testInterface interface{} testArr TestEncodingArr sub *SubObject } func (t *TestEncoding) IsNil() bool { return t == nil } func (t *TestEncoding) MarshalJSONObject(enc *Encoder) { enc.AddStringKey("test", t.test) enc.AddStringKey("test2", t.test2) enc.AddIntKey("testInt", t.testInt) enc.AddBoolKey("testBool", t.testBool) enc.AddInterfaceKey("testArr", t.testArr) enc.AddInterfaceKey("testF64", t.testF64) enc.AddInterfaceKey("testF32", t.testF32) enc.AddInterfaceKey("testInterface", t.testInterface) enc.AddInterfaceKey("sub", t.sub) } type SubObject struct { test1 int test2 string test3 float64 testBool bool sub *SubObject } func (t *SubObject) IsNil() bool { return t == nil } func (t *SubObject) MarshalJSONObject(enc *Encoder) { enc.AddIntKey("test1", t.test1) enc.AddStringKey("test2", t.test2) enc.AddFloatKey("test3", t.test3) enc.AddBoolKey("testBool", t.testBool) enc.AddObjectKey("sub", t.sub) } type testEncodingObjInterfaces struct { interfaceVal interface{} } func (t *testEncodingObjInterfaces) IsNil() bool { return t == nil } func (t *testEncodingObjInterfaces) MarshalJSONObject(enc *Encoder) { enc.AddInterfaceKey("interfaceVal", t.interfaceVal) } func TestEncoderObjectEncodeAPI(t *testing.T) { t.Run("encode-basic", func(t *testing.T) { builder := &strings.Builder{} enc := NewEncoder(builder) err := enc.EncodeObject(&testObject{ "漢字", nil, 1, nil, 1, nil, 1, nil, 1, nil, 1, nil, 1, nil, 1, nil, 1, nil, 1, nil, 1.1, nil, 1.1, nil, true, nil, &testObject{}, testSliceInts{}, interface{}("test"), }) assert.Nil(t, err, "Error should be nil") assert.Equal( t, `{"testStr":"漢字","testInt":1,"testInt64":1,"testInt32":1,"testInt16":1,"testInt8":1,"testUint64":1,"testUint32":1,"testUint16":1,"testUint8":1,"testFloat64":1.1,"testFloat32":1.1,"testBool":true}`, builder.String(), "Result of marshalling is different as the one expected", ) }) } func TestEncoderObjectMarshalAPI(t *testing.T) { t.Run("marshal-basic", func(t *testing.T) { r, err := Marshal(&testObject{ "漢字", nil, 1, nil, 1, nil, 1, nil, 1, nil, 1, nil, 1, nil, 1, nil, 1, nil, 1, nil, 1.1, nil, 1.1, nil, true, nil, &testObject{}, testSliceInts{}, []interface{}{"h", "o", "l", "a"}, }) assert.Nil(t, err, "Error should be nil") assert.Equal( t, `{"testStr":"漢字","testInt":1,"testInt64":1,"testInt32":1,"testInt16":1,"testInt8":1,"testUint64":1,"testUint32":1,"testUint16":1,"testUint8":1,"testFloat64":1.1,"testFloat32":1.1,"testBool":true}`, string(r), "Result of marshalling is different as the one expected", ) }) t.Run("marshal-complex", func(t *testing.T) { v := &TestEncoding{ test: "hello world", test2: "foobar", testInt: 1, testBool: true, testF32: 120.53, testF64: 120.15, testInterface: true, testArr: TestEncodingArr{ &TestEncoding{ test: "1", }, }, sub: &SubObject{ test1: 10, test2: "hello world", test3: 1.23543, testBool: true, sub: &SubObject{ test1: 10, testBool: false, test2: "hello world", }, }, } r, err := MarshalJSONObject(v) assert.Nil(t, err, "Error should be nil") assert.Equal( t, `{"test":"hello world","test2":"foobar","testInt":1,"testBool":true,"testArr":[{"test":"1","test2":"","testInt":0,"testBool":false,"testArr":[],"testF64":0,"testF32":0,"sub":{}}],"testF64":120.15,"testF32":120.53,"testInterface":true,"sub":{"test1":10,"test2":"hello world","test3":1.23543,"testBool":true,"sub":{"test1":10,"test2":"hello world","test3":0,"testBool":false,"sub":{}}}}`, string(r), "Result of marshalling is different as the one expected", ) }) t.Run("marshal-interface-string", func(t *testing.T) { v := testEncodingObjInterfaces{"string"} r, err := Marshal(&v) assert.Nil(t, err, "Error should be nil") assert.Equal( t, `{"interfaceVal":"string"}`, string(r), "Result of marshalling is different as the one expected") }) t.Run("marshal-interface-int", func(t *testing.T) { v := testEncodingObjInterfaces{1} r, err := Marshal(&v) assert.Nil(t, err, "Error should be nil") assert.Equal( t, `{"interfaceVal":1}`, string(r), "Result of marshalling is different as the one expected") }) t.Run("marshal-interface-int64", func(t *testing.T) { v := testEncodingObjInterfaces{int64(1)} r, err := Marshal(&v) assert.Nil(t, err, "Error should be nil") assert.Equal( t, `{"interfaceVal":1}`, string(r), "Result of marshalling is different as the one expected") }) t.Run("marshal-interface-int32", func(t *testing.T) { v := testEncodingObjInterfaces{int32(1)} r, err := Marshal(&v) assert.Nil(t, err, "Error should be nil") assert.Equal( t, `{"interfaceVal":1}`, string(r), "Result of marshalling is different as the one expected") }) t.Run("marshal-interface-int16", func(t *testing.T) { v := testEncodingObjInterfaces{int16(1)} r, err := Marshal(&v) assert.Nil(t, err, "Error should be nil") assert.Equal( t, `{"interfaceVal":1}`, string(r), "Result of marshalling is different as the one expected") }) t.Run("marshal-interface-int8", func(t *testing.T) { v := testEncodingObjInterfaces{int8(1)} r, err := Marshal(&v) assert.Nil(t, err, "Error should be nil") assert.Equal( t, `{"interfaceVal":1}`, string(r), "Result of marshalling is different as the one expected") }) t.Run("marshal-interface-uint64", func(t *testing.T) { v := testEncodingObjInterfaces{uint64(1)} r, err := Marshal(&v) assert.Nil(t, err, "Error should be nil") assert.Equal( t, `{"interfaceVal":1}`, string(r), "Result of marshalling is different as the one expected") }) t.Run("marshal-interface-uint32", func(t *testing.T) { v := testEncodingObjInterfaces{uint32(1)} r, err := Marshal(&v) assert.Nil(t, err, "Error should be nil") assert.Equal( t, `{"interfaceVal":1}`, string(r), "Result of marshalling is different as the one expected") }) t.Run("marshal-interface-uint16", func(t *testing.T) { v := testEncodingObjInterfaces{uint16(1)} r, err := Marshal(&v) assert.Nil(t, err, "Error should be nil") assert.Equal( t, `{"interfaceVal":1}`, string(r), "Result of marshalling is different as the one expected") }) t.Run("marshal-interface-uint8", func(t *testing.T) { v := testEncodingObjInterfaces{uint8(1)} r, err := Marshal(&v) assert.Nil(t, err, "Error should be nil") assert.Equal( t, `{"interfaceVal":1}`, string(r), "Result of marshalling is different as the one expected") }) t.Run("marshal-interface-float64", func(t *testing.T) { v := testEncodingObjInterfaces{float64(1.1)} r, err := Marshal(&v) assert.Nil(t, err, "Error should be nil") assert.Equal( t, `{"interfaceVal":1.1}`, string(r), "Result of marshalling is different as the one expected") }) t.Run("marshal-interface-float32", func(t *testing.T) { v := testEncodingObjInterfaces{float32(1.1)} r, err := Marshal(&v) assert.Nil(t, err, "Error should be nil") assert.Equal( t, `{"interfaceVal":1.1}`, string(r), "Result of marshalling is different as the one expected") }) t.Run("marshal-object-func", func(t *testing.T) { f := EncodeObjectFunc(func(enc *Encoder) { enc.AddStringKeyOmitEmpty("test", "test") }) r, err := Marshal(f) assert.Nil(t, err, "Error should be nil") assert.Equal( t, `{"test":"test"}`, string(r), "Result of marshalling is different as the one expected") }) t.Run("marshal-any-object", func(t *testing.T) { test := struct { Foo string Bar int }{ "test", 100, } r, err := MarshalAny(test) assert.Nil(t, err, "Error should be nil") assert.Equal( t, `{"Foo":"test","Bar":100}`, string(r), "Result of marshalling is different as the one expected") }) } type TestObectOmitEmpty struct { nonNiler int testInt int testFloat float64 testFloat32 float32 testString string testBool bool testObectOmitEmpty *TestObectOmitEmpty testObect *TestObectOmitEmpty } func (t *TestObectOmitEmpty) IsNil() bool { return t == nil } func (t *TestObectOmitEmpty) MarshalJSONObject(enc *Encoder) { enc.AddIntKeyOmitEmpty("testInt", t.testInt) enc.AddIntKeyOmitEmpty("testIntNotEmpty", 1) enc.AddFloatKeyOmitEmpty("testFloat", t.testFloat) enc.AddFloatKeyOmitEmpty("testFloatNotEmpty", 1.1) enc.AddFloat32KeyOmitEmpty("testFloat32", t.testFloat32) enc.AddFloat32KeyOmitEmpty("testFloat32NotEmpty", 1.1) enc.AddStringKeyOmitEmpty("testString", t.testString) enc.AddStringKeyOmitEmpty("testStringNotEmpty", "foo") enc.AddBoolKeyOmitEmpty("testBool", t.testBool) enc.AddBoolKeyOmitEmpty("testBoolNotEmpty", true) enc.AddObjectKeyOmitEmpty("testObect", t.testObect) enc.AddObjectKeyOmitEmpty("testObectOmitEmpty", t.testObectOmitEmpty) enc.AddArrayKeyOmitEmpty("testArrayOmitEmpty", TestEncodingArrStrings{}) enc.AddArrayKeyOmitEmpty("testArray", TestEncodingArrStrings{"foo"}) } type TestObectOmitEmptyInterface struct{} func (t *TestObectOmitEmptyInterface) IsNil() bool { return t == nil } func (t *TestObectOmitEmptyInterface) MarshalJSONObject(enc *Encoder) { enc.AddInterfaceKeyOmitEmpty("testInt", 0) enc.AddInterfaceKeyOmitEmpty("testInt64", int64(0)) enc.AddInterfaceKeyOmitEmpty("testInt32", int32(0)) enc.AddInterfaceKeyOmitEmpty("testInt16", int16(0)) enc.AddInterfaceKeyOmitEmpty("testInt8", int8(0)) enc.AddInterfaceKeyOmitEmpty("testUint8", uint8(0)) enc.AddInterfaceKeyOmitEmpty("testUint16", uint16(0)) enc.AddInterfaceKeyOmitEmpty("testUint32", uint32(0)) enc.AddInterfaceKeyOmitEmpty("testUint64", uint64(0)) enc.AddInterfaceKeyOmitEmpty("testIntNotEmpty", 1) enc.AddInterfaceKeyOmitEmpty("testFloat", 0) enc.AddInterfaceKeyOmitEmpty("testFloatNotEmpty", 1.1) enc.AddInterfaceKeyOmitEmpty("testFloat32", float32(0)) enc.AddInterfaceKeyOmitEmpty("testFloat32NotEmpty", float32(1.1)) enc.AddInterfaceKeyOmitEmpty("testString", "") enc.AddInterfaceKeyOmitEmpty("testStringNotEmpty", "foo") enc.AddInterfaceKeyOmitEmpty("testBool", false) enc.AddInterfaceKeyOmitEmpty("testBoolNotEmpty", true) enc.AddInterfaceKeyOmitEmpty("testObectOmitEmpty", nil) enc.AddInterfaceKeyOmitEmpty("testObect", &TestEncoding{}) enc.AddInterfaceKeyOmitEmpty("testArr", &TestEncodingArrStrings{}) } func TestEncoderObjectOmitEmpty(t *testing.T) { t.Run("encoder-omit-empty-all-types", func(t *testing.T) { v := &TestObectOmitEmpty{ nonNiler: 1, testInt: 0, testObect: &TestObectOmitEmpty{testInt: 1}, } r, err := MarshalJSONObject(v) assert.Nil(t, err, "Error should be nil") assert.Equal( t, `{"testIntNotEmpty":1,"testFloatNotEmpty":1.1,"testFloat32NotEmpty":1.1,"testStringNotEmpty":"foo","testBoolNotEmpty":true,"testObect":{"testInt":1,"testIntNotEmpty":1,"testFloatNotEmpty":1.1,"testFloat32NotEmpty":1.1,"testStringNotEmpty":"foo","testBoolNotEmpty":true,"testArray":["foo"]},"testArray":["foo"]}`, string(r), "Result of marshalling is different as the one expected", ) }) t.Run("encoder-omit-empty-interface", func(t *testing.T) { v := &TestObectOmitEmptyInterface{} r, err := MarshalJSONObject(v) assert.Nil(t, err, "Error should be nil") assert.Equal( t, `{"testIntNotEmpty":1,"testFloatNotEmpty":1.1,"testFloat32NotEmpty":1.1,"testStringNotEmpty":"foo","testBoolNotEmpty":true,"testObect":{"test":"","test2":"","testInt":0,"testBool":false,"testArr":[],"testF64":0,"testF32":0,"sub":{}}}`, string(r), "Result of marshalling is different as the one expected", ) }) } func TestEncoderObjectEncodeAPIError(t *testing.T) { t.Run("interface-key-error", func(t *testing.T) { builder := &strings.Builder{} enc := NewEncoder(builder) err := enc.EncodeObject(&testObjectWithUnknownType{struct{}{}}) assert.NotNil(t, err, "Error should not be nil") assert.Equal(t, "Invalid type struct {} provided to Marshal", err.Error(), "err.Error() should be 'Invalid type struct {} provided to Marshal'") }) t.Run("write-error", func(t *testing.T) { w := TestWriterError("") enc := NewEncoder(w) err := enc.EncodeObject(&testObject{}) assert.NotNil(t, err, "Error should not be nil") assert.Equal(t, "Test Error", err.Error(), "err.Error() should be 'Test Error'") }) t.Run("interface-error", func(t *testing.T) { builder := &strings.Builder{} enc := NewEncoder(builder) enc.AddInterfaceKeyOmitEmpty("test", struct{}{}) assert.NotNil(t, enc.err, "enc.Err() should not be nil") }) t.Run("pool-error", func(t *testing.T) { v := &TestEncoding{} enc := BorrowEncoder(nil) enc.isPooled = 1 defer func() { err := recover() assert.NotNil(t, err, "err shouldnt be nil") assert.IsType(t, InvalidUsagePooledEncoderError(""), err, "err should be of type InvalidUsagePooledEncoderError") assert.Equal(t, "Invalid usage of pooled encoder", err.(InvalidUsagePooledEncoderError).Error(), "err should be of type InvalidUsagePooledDecoderError") }() _ = enc.EncodeObject(v) assert.True(t, false, "should not be called as it should have panicked") }) } func TestEncoderObjectKeyNullEmpty(t *testing.T) { var testCases = []struct { name string baseJSON string expectedJSON string }{ { name: "basic 1st elem", baseJSON: "{", expectedJSON: `{"foo":null,"bar":{"test":"","test2":"","testInt":0,"testBool":false,"testArr":[],"testF64":0,"testF32":0,"sub":{}}`, }, { name: "basic 2nd elem", baseJSON: `{"test":"test"`, expectedJSON: `{"test":"test","foo":null,"bar":{"test":"","test2":"","testInt":0,"testBool":false,"testArr":[],"testF64":0,"testF32":0,"sub":{}}`, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { var b strings.Builder var enc = NewEncoder(&b) enc.writeString(testCase.baseJSON) enc.AddObjectKeyNullEmpty("foo", (*TestEncoding)(nil)) enc.ObjectKeyNullEmpty("bar", &TestEncoding{}) enc.Write() assert.Equal(t, testCase.expectedJSON, b.String()) }) } } func TestEncoderObjectNullEmpty(t *testing.T) { var testCases = []struct { name string baseJSON string expectedJSON string }{ { name: "basic 1st elem", baseJSON: "[", expectedJSON: `[null,{"test":"","test2":"","testInt":0,"testBool":false,"testArr":[],"testF64":0,"testF32":0,"sub":{}}`, }, { name: "basic 2nd elem", baseJSON: `["test"`, expectedJSON: `["test",null,{"test":"","test2":"","testInt":0,"testBool":false,"testArr":[],"testF64":0,"testF32":0,"sub":{}}`, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { var b strings.Builder var enc = NewEncoder(&b) enc.writeString(testCase.baseJSON) enc.AddObjectNullEmpty((*TestEncoding)(nil)) enc.ObjectNullEmpty(&TestEncoding{}) enc.Write() assert.Equal(t, testCase.expectedJSON, b.String()) }) } } type ObjectWithKeys struct { Str string Int int Int64 int64 Int32 int32 Int16 int16 Int8 int8 Uint64 uint64 Uint32 uint32 Uint16 uint16 Uint8 uint8 Float64 float64 Float32 float32 Bool bool Obj *ObjectWithKeys Slice TestEncodingArrStrings Time *time.Time EmbeddedJSON *EmbeddedJSON } func (o *ObjectWithKeys) MarshalJSONObject(enc *Encoder) { enc.StringKey("string", o.Str) enc.StringKeyOmitEmpty("string", o.Str) enc.StringKeyNullEmpty("string", o.Str) enc.IntKey("int", o.Int) enc.IntKeyOmitEmpty("int", o.Int) enc.IntKeyNullEmpty("int", o.Int) enc.Int64Key("int64", o.Int64) enc.Int64KeyOmitEmpty("int64", o.Int64) enc.Int64KeyNullEmpty("int64", o.Int64) enc.Int32Key("int32", o.Int32) enc.Int32KeyOmitEmpty("int32", o.Int32) enc.Int32KeyNullEmpty("int32", o.Int32) enc.Int16Key("int16", o.Int16) enc.Int16KeyOmitEmpty("int16", o.Int16) enc.Int16KeyNullEmpty("int16", o.Int16) enc.Int8Key("int8", o.Int8) enc.Int8KeyOmitEmpty("int8", o.Int8) enc.Int8KeyNullEmpty("int8", o.Int8) enc.Uint64KeyOmitEmpty("uint64", o.Uint64) enc.Uint64KeyNullEmpty("uint64", o.Uint64) enc.Uint64Key("uint64", o.Uint64) enc.Uint32Key("uint32", o.Uint32) enc.Uint32KeyOmitEmpty("uint32", o.Uint32) enc.Uint32KeyNullEmpty("uint32", o.Uint32) enc.Uint16KeyOmitEmpty("uint16", o.Uint16) enc.Uint16KeyNullEmpty("uint16", o.Uint16) enc.Uint16Key("uint16", o.Uint16) enc.Uint8Key("uint8", o.Uint8) enc.Uint8KeyOmitEmpty("uint8", o.Uint8) enc.Uint8KeyNullEmpty("uint8", o.Uint8) enc.Float64Key("float64", o.Float64) enc.Float64KeyOmitEmpty("float64", o.Float64) enc.Float64KeyNullEmpty("float64", o.Float64) enc.Float32Key("float32", o.Float32) enc.Float32KeyOmitEmpty("float32", o.Float32) enc.Float32KeyNullEmpty("float32", o.Float32) enc.BoolKey("bool", o.Bool) enc.BoolKeyOmitEmpty("bool", o.Bool) enc.BoolKeyNullEmpty("bool", o.Bool) enc.ObjectKeyOmitEmpty("object", o.Obj) enc.ObjectKeyNullEmpty("object", o.Obj) enc.ObjectKey("object", o.Obj) enc.ArrayKey("array", o.Slice) enc.ArrayKeyOmitEmpty("array", o.Slice) enc.ArrayKeyNullEmpty("array", o.Slice) enc.TimeKey("time", o.Time, time.RFC3339) enc.AddEmbeddedJSONKey("ejson", o.EmbeddedJSON) enc.AddEmbeddedJSONKeyOmitEmpty("ejson", o.EmbeddedJSON) enc.NullKey("null") } func (o *ObjectWithKeys) IsNil() bool { return o == nil } type NilObject struct{} func (n *NilObject) MarshalJSONObject(enc *Encoder) {} func (n *NilObject) IsNil() bool { return true } func TestEncodeObjectWithKeys(t *testing.T) { t.Run( "should not encode any key", func(t *testing.T) { var b strings.Builder var enc = NewEncoder(&b) var o = &ObjectWithKeys{} var err = enc.EncodeObjectKeys(o, []string{}) assert.Nil(t, err) assert.Equal(t, `{}`, b.String()) }, ) t.Run( "should encode some keys", func(t *testing.T) { var b strings.Builder var enc = NewEncoder(&b) var o = &ObjectWithKeys{Str: "hello", Int: 420} var err = enc.EncodeObjectKeys(o, []string{"string", "int"}) assert.Nil(t, err) assert.Equal( t, `{"string":"hello","string":"hello","string":"hello","int":420,"int":420,"int":420}`, b.String(), ) }, ) t.Run("write-error", func(t *testing.T) { w := TestWriterError("") enc := NewEncoder(w) o := &ObjectWithKeys{Str: "hello", Int: 420} err := enc.EncodeObjectKeys(o, []string{"string", "int"}) assert.NotNil(t, err, "Error should not be nil") assert.Equal(t, "Test Error", err.Error(), "err.Error() should be 'Test Error'") }) t.Run("pool-error", func(t *testing.T) { v := &TestEncoding{} enc := BorrowEncoder(nil) enc.isPooled = 1 defer func() { err := recover() assert.NotNil(t, err, "err shouldnt be nil") assert.IsType(t, InvalidUsagePooledEncoderError(""), err, "err should be of type InvalidUsagePooledEncoderError") assert.Equal(t, "Invalid usage of pooled encoder", err.(InvalidUsagePooledEncoderError).Error(), "err should be of type InvalidUsagePooledDecoderError") }() _ = enc.EncodeObjectKeys(v, []string{}) assert.True(t, false, "should not be called as it should have panicked") }) t.Run("interface-key-error", func(t *testing.T) { builder := &strings.Builder{} enc := NewEncoder(builder) err := enc.EncodeObjectKeys(&testObjectWithUnknownType{struct{}{}}, []string{}) assert.NotNil(t, err, "Error should not be nil") assert.Equal(t, "Invalid type struct {} provided to Marshal", err.Error(), "err.Error() should be 'Invalid type struct {} provided to Marshal'") }) t.Run("encode-object-with-keys", func(t *testing.T) { b := &strings.Builder{} enc := NewEncoder(b) err := enc.EncodeObjectKeys(EncodeObjectFunc(func(enc *Encoder) { enc.ObjectKeyWithKeys("test", EncodeObjectFunc(func(enc *Encoder) { enc.StringKey("test", "hello") enc.StringKey("test2", "hello") }), []string{"test"}) }), []string{}) assert.Nil(t, err, "Error should not be nil") assert.Equal(t, `{}`, b.String()) }) t.Run("encode-object-with-keys", func(t *testing.T) { b := &strings.Builder{} enc := NewEncoder(b) err := enc.EncodeObject(EncodeObjectFunc(func(enc *Encoder) { enc.ObjectKeyWithKeys("test", EncodeObjectFunc(func(enc *Encoder) { enc.keys = nil enc.StringKey("test", "hello") enc.StringKey("test2", "hello") }), []string{"test"}) })) assert.Nil(t, err, "Error should not be nil") assert.Equal(t, `{"test":{}}`, b.String()) }) t.Run("encode-object-with-keys", func(t *testing.T) { b := &strings.Builder{} enc := NewEncoder(b) err := enc.EncodeObject(EncodeObjectFunc(func(enc *Encoder) { enc.ObjectKeyWithKeys("test", EncodeObjectFunc(func(enc *Encoder) { enc.StringKey("test", "hello") enc.StringKey("test2", "hello") }), []string{"test"}) })) assert.Nil(t, err, "Error should not be nil") assert.Equal(t, `{"test":{"test":"hello"}}`, b.String()) }) t.Run("encode-object-with-keys", func(t *testing.T) { b := &strings.Builder{} enc := NewEncoder(b) err := enc.EncodeObject(EncodeObjectFunc(func(enc *Encoder) { enc.writeByte(' ') enc.ObjectKeyWithKeys("test", EncodeObjectFunc(func(enc *Encoder) { enc.StringKey("test", "hello") enc.StringKey("test2", "hello") }), []string{"test"}) })) assert.Nil(t, err, "Error should not be nil") assert.Equal(t, `{ ,"test":{"test":"hello"}}`, b.String()) }) t.Run("encode-object-with-keys", func(t *testing.T) { b := &strings.Builder{} enc := NewEncoder(b) err := enc.EncodeObject(EncodeObjectFunc(func(enc *Encoder) { enc.writeByte(' ') enc.ObjectKeyWithKeys("test", &NilObject{}, []string{}) })) assert.Nil(t, err, "Error should not be nil") assert.Equal(t, `{ ,"test":{}}`, b.String()) }) t.Run("encode-object-with-keys", func(t *testing.T) { b := &strings.Builder{} enc := NewEncoder(b) err := enc.EncodeArray(EncodeArrayFunc(func(enc *Encoder) { enc.ObjectWithKeys(EncodeObjectFunc(func(enc *Encoder) { enc.StringKey("test", "hello") enc.StringKey("test2", "hello") }), []string{"test"}) })) assert.Nil(t, err, "Error should not be nil") assert.Equal(t, `[{"test":"hello"}]`, b.String()) }) t.Run("encode-object-with-keys", func(t *testing.T) { b := &strings.Builder{} enc := NewEncoder(b) err := enc.EncodeArray(EncodeArrayFunc(func(enc *Encoder) { enc.writeByte(' ') enc.ObjectWithKeys(EncodeObjectFunc(func(enc *Encoder) { enc.StringKey("test", "hello") enc.StringKey("test2", "hello") }), []string{"test"}) })) assert.Nil(t, err, "Error should not be nil") assert.Equal(t, `[ ,{"test":"hello"}]`, b.String()) }) t.Run("encode-object-with-keys", func(t *testing.T) { b := &strings.Builder{} enc := NewEncoder(b) err := enc.EncodeArray(EncodeArrayFunc(func(enc *Encoder) { enc.ObjectWithKeys(&NilObject{}, []string{}) })) assert.Nil(t, err, "Error should not be nil") assert.Equal(t, `[{}]`, b.String()) }) t.Run("encode-object-with-keys", func(t *testing.T) { b := &strings.Builder{} enc := NewEncoder(b) err := enc.EncodeArray(EncodeArrayFunc(func(enc *Encoder) { enc.writeByte(' ') enc.ObjectWithKeys(&NilObject{}, []string{}) })) assert.Nil(t, err, "Error should not be nil") assert.Equal(t, `[ ,{}]`, b.String()) }) } golang-github-francoispqt-gojay-1.2.13/encode_pool.go000066400000000000000000000015421365331665100226130ustar00rootroot00000000000000package gojay import ( "io" "sync" ) var encPool = sync.Pool{ New: func() interface{} { return NewEncoder(nil) }, } var streamEncPool = sync.Pool{ New: func() interface{} { return Stream.NewEncoder(nil) }, } func init() { for i := 0; i < 32; i++ { encPool.Put(NewEncoder(nil)) } for i := 0; i < 32; i++ { streamEncPool.Put(Stream.NewEncoder(nil)) } } // NewEncoder returns a new encoder or borrows one from the pool func NewEncoder(w io.Writer) *Encoder { return &Encoder{w: w} } // BorrowEncoder borrows an Encoder from the pool. func BorrowEncoder(w io.Writer) *Encoder { enc := encPool.Get().(*Encoder) enc.w = w enc.buf = enc.buf[:0] enc.isPooled = 0 enc.err = nil enc.hasKeys = false enc.keys = nil return enc } // Release sends back a Encoder to the pool. func (enc *Encoder) Release() { enc.isPooled = 1 encPool.Put(enc) } golang-github-francoispqt-gojay-1.2.13/encode_pool_test.go000066400000000000000000000007161365331665100236540ustar00rootroot00000000000000package gojay import ( "fmt" "log" "strconv" "testing" "time" ) func TestConcurrencyMarshal(t *testing.T) { var f = func(num int, t *testing.T) { for { b, err := Marshal(num) if err != nil { log.Fatal(err) } s := string(b) if n, err := strconv.Atoi(s); err != nil || n != num { t.Error(fmt.Errorf( "caught race: %v %v", s, num, )) } } } for i := 0; i < 100; i++ { go f(i, t) } time.Sleep(2 * time.Second) } golang-github-francoispqt-gojay-1.2.13/encode_slice.go000066400000000000000000000050661365331665100227460ustar00rootroot00000000000000package gojay // AddSliceString marshals the given []string s func (enc *Encoder) AddSliceString(s []string) { enc.SliceString(s) } // SliceString marshals the given []string s func (enc *Encoder) SliceString(s []string) { enc.Array(EncodeArrayFunc(func(enc *Encoder) { for _, str := range s { enc.String(str) } })) } // AddSliceStringKey marshals the given []string s func (enc *Encoder) AddSliceStringKey(k string, s []string) { enc.SliceStringKey(k, s) } // SliceStringKey marshals the given []string s func (enc *Encoder) SliceStringKey(k string, s []string) { enc.ArrayKey(k, EncodeArrayFunc(func(enc *Encoder) { for _, str := range s { enc.String(str) } })) } // AddSliceInt marshals the given []int s func (enc *Encoder) AddSliceInt(s []int) { enc.SliceInt(s) } // SliceInt marshals the given []int s func (enc *Encoder) SliceInt(s []int) { enc.Array(EncodeArrayFunc(func(enc *Encoder) { for _, i := range s { enc.Int(i) } })) } // AddSliceIntKey marshals the given []int s func (enc *Encoder) AddSliceIntKey(k string, s []int) { enc.SliceIntKey(k, s) } // SliceIntKey marshals the given []int s func (enc *Encoder) SliceIntKey(k string, s []int) { enc.ArrayKey(k, EncodeArrayFunc(func(enc *Encoder) { for _, i := range s { enc.Int(i) } })) } // AddSliceFloat64 marshals the given []float64 s func (enc *Encoder) AddSliceFloat64(s []float64) { enc.SliceFloat64(s) } // SliceFloat64 marshals the given []float64 s func (enc *Encoder) SliceFloat64(s []float64) { enc.Array(EncodeArrayFunc(func(enc *Encoder) { for _, i := range s { enc.Float64(i) } })) } // AddSliceFloat64Key marshals the given []float64 s func (enc *Encoder) AddSliceFloat64Key(k string, s []float64) { enc.SliceFloat64Key(k, s) } // SliceFloat64Key marshals the given []float64 s func (enc *Encoder) SliceFloat64Key(k string, s []float64) { enc.ArrayKey(k, EncodeArrayFunc(func(enc *Encoder) { for _, i := range s { enc.Float64(i) } })) } // AddSliceBool marshals the given []bool s func (enc *Encoder) AddSliceBool(s []bool) { enc.SliceBool(s) } // SliceBool marshals the given []bool s func (enc *Encoder) SliceBool(s []bool) { enc.Array(EncodeArrayFunc(func(enc *Encoder) { for _, i := range s { enc.Bool(i) } })) } // AddSliceBoolKey marshals the given []bool s func (enc *Encoder) AddSliceBoolKey(k string, s []bool) { enc.SliceBoolKey(k, s) } // SliceBoolKey marshals the given []bool s func (enc *Encoder) SliceBoolKey(k string, s []bool) { enc.ArrayKey(k, EncodeArrayFunc(func(enc *Encoder) { for _, i := range s { enc.Bool(i) } })) } golang-github-francoispqt-gojay-1.2.13/encode_slice_test.go000066400000000000000000000066161365331665100240070ustar00rootroot00000000000000package gojay import ( "strings" "testing" "github.com/stretchr/testify/require" ) func (s *slicesTestObject) MarshalJSONObject(enc *Encoder) { enc.AddSliceStringKey("sliceString", s.sliceString) enc.AddSliceIntKey("sliceInt", s.sliceInt) enc.AddSliceFloat64Key("sliceFloat64", s.sliceFloat64) enc.AddSliceBoolKey("sliceBool", s.sliceBool) } func (s *slicesTestObject) IsNil() bool { return s == nil } func TestEncodeSlices(t *testing.T) { testCases := []struct { name string json string obj slicesTestObject }{ { name: "basic slice string", json: `{ "sliceString": ["foo","bar"], "sliceInt": [], "sliceFloat64": [], "sliceBool": [] }`, obj: slicesTestObject{ sliceString: []string{"foo", "bar"}, }, }, { name: "basic slice bool", json: `{ "sliceString": [], "sliceInt": [], "sliceFloat64": [], "sliceBool": [true,false] }`, obj: slicesTestObject{ sliceBool: []bool{true, false}, }, }, { name: "basic slice int", json: `{ "sliceString": [], "sliceFloat64": [], "sliceInt": [1,2,3], "sliceBool": [] }`, obj: slicesTestObject{ sliceInt: []int{1, 2, 3}, }, }, { name: "basic slice float64", json: `{ "sliceString": [], "sliceFloat64": [1.3,2.4,3.1], "sliceInt": [], "sliceBool": [] }`, obj: slicesTestObject{ sliceFloat64: []float64{1.3, 2.4, 3.1}, }, }, } for _, testCase := range testCases { t.Run( testCase.name, func(t *testing.T) { b := strings.Builder{} enc := BorrowEncoder(&b) err := enc.Encode(&testCase.obj) require.Nil(t, err, "err should be nil") require.JSONEq(t, testCase.json, b.String()) }, ) } } type testSliceSliceString [][]string func (t testSliceSliceString) MarshalJSONArray(enc *Encoder) { for _, s := range t { enc.AddSliceString(s) } } func (t testSliceSliceString) IsNil() bool { return t == nil } type testSliceSliceBool [][]bool func (t testSliceSliceBool) MarshalJSONArray(enc *Encoder) { for _, s := range t { enc.AddSliceBool(s) } } func (t testSliceSliceBool) IsNil() bool { return t == nil } type testSliceSliceInt [][]int func (t testSliceSliceInt) MarshalJSONArray(enc *Encoder) { for _, s := range t { enc.AddSliceInt(s) } } func (t testSliceSliceInt) IsNil() bool { return t == nil } type testSliceSliceFloat64 [][]float64 func (t testSliceSliceFloat64) MarshalJSONArray(enc *Encoder) { for _, s := range t { enc.AddSliceFloat64(s) } } func (t testSliceSliceFloat64) IsNil() bool { return t == nil } func TestEncodeSliceSlices(t *testing.T) { testCases := []struct { name string s MarshalerJSONArray json string }{ { name: "slice of strings", s: testSliceSliceString{ []string{"foo", "bar"}, }, json: `[["foo","bar"]]`, }, { name: "slice of ints", s: testSliceSliceInt{ []int{1, 2}, }, json: `[[1,2]]`, }, { name: "slice of float", s: testSliceSliceFloat64{ []float64{1.1, 1.2}, }, json: `[[1.1,1.2]]`, }, { name: "slice of bool", s: testSliceSliceBool{ []bool{true, false}, }, json: `[[true,false]]`, }, } for _, testCase := range testCases { t.Run( testCase.name, func(t *testing.T) { b := strings.Builder{} enc := BorrowEncoder(&b) err := enc.Encode(testCase.s) require.Nil(t, err, "err should be nil") require.JSONEq(t, testCase.json, b.String()) }, ) } } golang-github-francoispqt-gojay-1.2.13/encode_sqlnull.go000066400000000000000000000316301365331665100233350ustar00rootroot00000000000000package gojay import "database/sql" // EncodeSQLNullString encodes a string to func (enc *Encoder) EncodeSQLNullString(v *sql.NullString) error { if enc.isPooled == 1 { panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder")) } _, _ = enc.encodeString(v.String) _, err := enc.Write() if err != nil { enc.err = err return err } return nil } // AddSQLNullString adds a string to be encoded, must be used inside a slice or array encoding (does not encode a key) func (enc *Encoder) AddSQLNullString(v *sql.NullString) { enc.String(v.String) } // AddSQLNullStringOmitEmpty adds a string to be encoded or skips it if it is zero value. // Must be used inside a slice or array encoding (does not encode a key) func (enc *Encoder) AddSQLNullStringOmitEmpty(v *sql.NullString) { if v != nil && v.Valid && v.String != "" { enc.StringOmitEmpty(v.String) } } // AddSQLNullStringNullEmpty adds a string to be encoded or skips it if it is zero value. // Must be used inside a slice or array encoding (does not encode a key) func (enc *Encoder) AddSQLNullStringNullEmpty(v *sql.NullString) { if v != nil && v.Valid { enc.StringNullEmpty(v.String) } } // AddSQLNullStringKey adds a string to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) AddSQLNullStringKey(key string, v *sql.NullString) { enc.StringKey(key, v.String) } // AddSQLNullStringKeyOmitEmpty adds a string to be encoded or skips it if it is zero value. // Must be used inside an object as it will encode a key func (enc *Encoder) AddSQLNullStringKeyOmitEmpty(key string, v *sql.NullString) { if v != nil && v.Valid && v.String != "" { enc.StringKeyOmitEmpty(key, v.String) } } // SQLNullString adds a string to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) SQLNullString(v *sql.NullString) { enc.String(v.String) } // SQLNullStringOmitEmpty adds a string to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) SQLNullStringOmitEmpty(v *sql.NullString) { if v != nil && v.Valid && v.String != "" { enc.String(v.String) } } // SQLNullStringNullEmpty adds a string to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) SQLNullStringNullEmpty(v *sql.NullString) { if v != nil && v.Valid { enc.StringNullEmpty(v.String) } } // SQLNullStringKey adds a string to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) SQLNullStringKey(key string, v *sql.NullString) { enc.StringKey(key, v.String) } // SQLNullStringKeyOmitEmpty adds a string to be encoded or skips it if it is zero value. // Must be used inside an object as it will encode a key func (enc *Encoder) SQLNullStringKeyOmitEmpty(key string, v *sql.NullString) { if v != nil && v.Valid && v.String != "" { enc.StringKeyOmitEmpty(key, v.String) } } // SQLNullStringKeyNullEmpty adds a string to be encoded or skips it if it is zero value. // Must be used inside an object as it will encode a key func (enc *Encoder) SQLNullStringKeyNullEmpty(key string, v *sql.NullString) { if v != nil && v.Valid { enc.StringKeyNullEmpty(key, v.String) } } // NullInt64 // EncodeSQLNullInt64 encodes a string to func (enc *Encoder) EncodeSQLNullInt64(v *sql.NullInt64) error { if enc.isPooled == 1 { panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder")) } _, _ = enc.encodeInt64(v.Int64) _, err := enc.Write() if err != nil { enc.err = err return err } return nil } // AddSQLNullInt64 adds a string to be encoded, must be used inside a slice or array encoding (does not encode a key) func (enc *Encoder) AddSQLNullInt64(v *sql.NullInt64) { enc.Int64(v.Int64) } // AddSQLNullInt64OmitEmpty adds a string to be encoded or skips it if it is zero value. // Must be used inside a slice or array encoding (does not encode a key) func (enc *Encoder) AddSQLNullInt64OmitEmpty(v *sql.NullInt64) { if v != nil && v.Valid && v.Int64 != 0 { enc.Int64OmitEmpty(v.Int64) } } // AddSQLNullInt64NullEmpty adds a string to be encoded or skips it if it is zero value. // Must be used inside a slice or array encoding (does not encode a key) func (enc *Encoder) AddSQLNullInt64NullEmpty(v *sql.NullInt64) { if v != nil && v.Valid { enc.Int64NullEmpty(v.Int64) } } // AddSQLNullInt64Key adds a string to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) AddSQLNullInt64Key(key string, v *sql.NullInt64) { enc.Int64Key(key, v.Int64) } // AddSQLNullInt64KeyOmitEmpty adds a string to be encoded or skips it if it is zero value. // Must be used inside an object as it will encode a key func (enc *Encoder) AddSQLNullInt64KeyOmitEmpty(key string, v *sql.NullInt64) { if v != nil && v.Valid && v.Int64 != 0 { enc.Int64KeyOmitEmpty(key, v.Int64) } } // AddSQLNullInt64KeyNullEmpty adds a string to be encoded or skips it if it is zero value. // Must be used inside an object as it will encode a key func (enc *Encoder) AddSQLNullInt64KeyNullEmpty(key string, v *sql.NullInt64) { if v != nil && v.Valid { enc.Int64KeyNullEmpty(key, v.Int64) } } // SQLNullInt64 adds a string to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) SQLNullInt64(v *sql.NullInt64) { enc.Int64(v.Int64) } // SQLNullInt64OmitEmpty adds a string to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) SQLNullInt64OmitEmpty(v *sql.NullInt64) { if v != nil && v.Valid && v.Int64 != 0 { enc.Int64(v.Int64) } } // SQLNullInt64NullEmpty adds a string to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) SQLNullInt64NullEmpty(v *sql.NullInt64) { if v != nil && v.Valid { enc.Int64NullEmpty(v.Int64) } } // SQLNullInt64Key adds a string to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) SQLNullInt64Key(key string, v *sql.NullInt64) { enc.Int64Key(key, v.Int64) } // SQLNullInt64KeyOmitEmpty adds a string to be encoded or skips it if it is zero value. // Must be used inside an object as it will encode a key func (enc *Encoder) SQLNullInt64KeyOmitEmpty(key string, v *sql.NullInt64) { if v != nil && v.Valid && v.Int64 != 0 { enc.Int64KeyOmitEmpty(key, v.Int64) } } // SQLNullInt64KeyNullEmpty adds a string to be encoded or skips it if it is zero value. // Must be used inside an object as it will encode a key func (enc *Encoder) SQLNullInt64KeyNullEmpty(key string, v *sql.NullInt64) { if v != nil && v.Valid { enc.Int64KeyNullEmpty(key, v.Int64) } } // NullFloat64 // EncodeSQLNullFloat64 encodes a string to func (enc *Encoder) EncodeSQLNullFloat64(v *sql.NullFloat64) error { if enc.isPooled == 1 { panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder")) } _, _ = enc.encodeFloat(v.Float64) _, err := enc.Write() if err != nil { enc.err = err return err } return nil } // AddSQLNullFloat64 adds a string to be encoded, must be used inside a slice or array encoding (does not encode a key) func (enc *Encoder) AddSQLNullFloat64(v *sql.NullFloat64) { enc.Float64(v.Float64) } // AddSQLNullFloat64OmitEmpty adds a string to be encoded or skips it if it is zero value. // Must be used inside a slice or array encoding (does not encode a key) func (enc *Encoder) AddSQLNullFloat64OmitEmpty(v *sql.NullFloat64) { if v != nil && v.Valid && v.Float64 != 0 { enc.Float64OmitEmpty(v.Float64) } } // AddSQLNullFloat64NullEmpty adds a string to be encoded or skips it if it is zero value. // Must be used inside a slice or array encoding (does not encode a key) func (enc *Encoder) AddSQLNullFloat64NullEmpty(v *sql.NullFloat64) { if v != nil && v.Valid { enc.Float64NullEmpty(v.Float64) } } // AddSQLNullFloat64Key adds a string to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) AddSQLNullFloat64Key(key string, v *sql.NullFloat64) { enc.Float64Key(key, v.Float64) } // AddSQLNullFloat64KeyOmitEmpty adds a string to be encoded or skips it if it is zero value. // Must be used inside an object as it will encode a key func (enc *Encoder) AddSQLNullFloat64KeyOmitEmpty(key string, v *sql.NullFloat64) { if v != nil && v.Valid && v.Float64 != 0 { enc.Float64KeyOmitEmpty(key, v.Float64) } } // AddSQLNullFloat64KeyNullEmpty adds a string to be encoded or skips it if it is zero value. // Must be used inside an object as it will encode a key func (enc *Encoder) AddSQLNullFloat64KeyNullEmpty(key string, v *sql.NullFloat64) { if v != nil && v.Valid { enc.Float64KeyNullEmpty(key, v.Float64) } } // SQLNullFloat64 adds a string to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) SQLNullFloat64(v *sql.NullFloat64) { enc.Float64(v.Float64) } // SQLNullFloat64OmitEmpty adds a string to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) SQLNullFloat64OmitEmpty(v *sql.NullFloat64) { if v != nil && v.Valid && v.Float64 != 0 { enc.Float64(v.Float64) } } // SQLNullFloat64NullEmpty adds a string to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) SQLNullFloat64NullEmpty(v *sql.NullFloat64) { if v != nil && v.Valid { enc.Float64NullEmpty(v.Float64) } } // SQLNullFloat64Key adds a string to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) SQLNullFloat64Key(key string, v *sql.NullFloat64) { enc.Float64Key(key, v.Float64) } // SQLNullFloat64KeyOmitEmpty adds a string to be encoded or skips it if it is zero value. // Must be used inside an object as it will encode a key func (enc *Encoder) SQLNullFloat64KeyOmitEmpty(key string, v *sql.NullFloat64) { if v != nil && v.Valid && v.Float64 != 0 { enc.Float64KeyOmitEmpty(key, v.Float64) } } // SQLNullFloat64KeyNullEmpty adds a string to be encoded or skips it if it is zero value. // Must be used inside an object as it will encode a key func (enc *Encoder) SQLNullFloat64KeyNullEmpty(key string, v *sql.NullFloat64) { if v != nil && v.Valid { enc.Float64KeyNullEmpty(key, v.Float64) } } // NullBool // EncodeSQLNullBool encodes a string to func (enc *Encoder) EncodeSQLNullBool(v *sql.NullBool) error { if enc.isPooled == 1 { panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder")) } _, _ = enc.encodeBool(v.Bool) _, err := enc.Write() if err != nil { enc.err = err return err } return nil } // AddSQLNullBool adds a string to be encoded, must be used inside a slice or array encoding (does not encode a key) func (enc *Encoder) AddSQLNullBool(v *sql.NullBool) { enc.Bool(v.Bool) } // AddSQLNullBoolOmitEmpty adds a string to be encoded or skips it if it is zero value. // Must be used inside a slice or array encoding (does not encode a key) func (enc *Encoder) AddSQLNullBoolOmitEmpty(v *sql.NullBool) { if v != nil && v.Valid && v.Bool != false { enc.BoolOmitEmpty(v.Bool) } } // AddSQLNullBoolKey adds a string to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) AddSQLNullBoolKey(key string, v *sql.NullBool) { enc.BoolKey(key, v.Bool) } // AddSQLNullBoolKeyOmitEmpty adds a string to be encoded or skips it if it is zero value. // Must be used inside an object as it will encode a key func (enc *Encoder) AddSQLNullBoolKeyOmitEmpty(key string, v *sql.NullBool) { if v != nil && v.Valid && v.Bool != false { enc.BoolKeyOmitEmpty(key, v.Bool) } } // AddSQLNullBoolKeyNullEmpty adds a string to be encoded or skips it if it is zero value. // Must be used inside an object as it will encode a key func (enc *Encoder) AddSQLNullBoolKeyNullEmpty(key string, v *sql.NullBool) { if v != nil && v.Valid { enc.BoolKeyNullEmpty(key, v.Bool) } } // SQLNullBool adds a string to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) SQLNullBool(v *sql.NullBool) { enc.Bool(v.Bool) } // SQLNullBoolOmitEmpty adds a string to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) SQLNullBoolOmitEmpty(v *sql.NullBool) { if v != nil && v.Valid && v.Bool != false { enc.Bool(v.Bool) } } // SQLNullBoolNullEmpty adds a string to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) SQLNullBoolNullEmpty(v *sql.NullBool) { if v != nil && v.Valid { enc.BoolNullEmpty(v.Bool) } } // SQLNullBoolKey adds a string to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) SQLNullBoolKey(key string, v *sql.NullBool) { enc.BoolKey(key, v.Bool) } // SQLNullBoolKeyOmitEmpty adds a string to be encoded or skips it if it is zero value. // Must be used inside an object as it will encode a key func (enc *Encoder) SQLNullBoolKeyOmitEmpty(key string, v *sql.NullBool) { if v != nil && v.Valid && v.Bool != false { enc.BoolKeyOmitEmpty(key, v.Bool) } } // SQLNullBoolKeyNullEmpty adds a string to be encoded or skips it if it is zero value. // Must be used inside an object as it will encode a key func (enc *Encoder) SQLNullBoolKeyNullEmpty(key string, v *sql.NullBool) { if v != nil && v.Valid { enc.BoolKeyNullEmpty(key, v.Bool) } } golang-github-francoispqt-gojay-1.2.13/encode_sqlnull_test.go000066400000000000000000001014441365331665100243750ustar00rootroot00000000000000package gojay import ( "database/sql" "strings" "testing" "github.com/stretchr/testify/assert" ) // Null String func TestEncoceSQLNullString(t *testing.T) { testCases := []struct { name string sqlNullString sql.NullString expectedResult string err bool }{ { name: "it should encode a null string", sqlNullString: sql.NullString{ String: "foo bar", }, expectedResult: `"foo bar"`, }, { name: "it should return an err as the string is invalid", sqlNullString: sql.NullString{ String: "foo \t bar", }, expectedResult: `"foo \t bar"`, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { var b strings.Builder enc := NewEncoder(&b) err := enc.EncodeSQLNullString(&testCase.sqlNullString) if testCase.err { assert.NotNil(t, err) } else { assert.Nil(t, err) assert.Equal(t, testCase.expectedResult, b.String()) } }) } t.Run( "should panic as the encoder is pooled", func(t *testing.T) { builder := &strings.Builder{} enc := NewEncoder(builder) enc.isPooled = 1 defer func() { err := recover() assert.NotNil(t, err, "err should not be nil") assert.IsType(t, InvalidUsagePooledEncoderError(""), err, "err should be of type InvalidUsagePooledEncoderError") }() _ = enc.EncodeSQLNullString(&sql.NullString{}) assert.True(t, false, "should not be called as encoder should have panicked") }, ) t.Run( "should return an error as the writer encounters an error", func(t *testing.T) { builder := TestWriterError("") enc := NewEncoder(builder) err := enc.EncodeSQLNullString(&sql.NullString{}) assert.NotNil(t, err) }, ) } func TestAddSQLNullStringKey(t *testing.T) { t.Run( "AddSQLNullStringKey", func(t *testing.T) { testCases := []struct { name string sqlNullString sql.NullString baseJSON string expectedResult string err bool }{ { name: "it should encode a null string", sqlNullString: sql.NullString{ String: "foo bar", }, baseJSON: "{", expectedResult: `{"foo":"foo bar"`, }, { name: "it should encode a null string", sqlNullString: sql.NullString{ String: "foo \t bar", }, baseJSON: "{", expectedResult: `{"foo":"foo \t bar"`, }, { name: "it should encode a null string", sqlNullString: sql.NullString{ String: "foo \t bar", }, baseJSON: "{", expectedResult: `{"foo":"foo \t bar"`, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { var b strings.Builder enc := NewEncoder(&b) enc.writeString(testCase.baseJSON) enc.AddSQLNullStringKey("foo", &testCase.sqlNullString) enc.Write() assert.Equal(t, testCase.expectedResult, b.String()) var b2 strings.Builder enc = NewEncoder(&b2) enc.writeString(testCase.baseJSON) enc.SQLNullStringKey("foo", &testCase.sqlNullString) enc.Write() assert.Equal(t, testCase.expectedResult, b2.String()) }) } }, ) t.Run( "AddSQLNullStringKeyOmitEmpty, is should encode a sql.NullString", func(t *testing.T) { testCases := []struct { name string sqlNullString sql.NullString baseJSON string expectedResult string err bool }{ { name: "it should encode a null string", sqlNullString: sql.NullString{ String: "foo bar", Valid: true, }, baseJSON: "{", expectedResult: `{"foo":"foo bar"`, }, { name: "it should not encode anything as null string is invalid", sqlNullString: sql.NullString{ String: "foo \t bar", Valid: false, }, baseJSON: "{", expectedResult: `{`, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { var b strings.Builder enc := NewEncoder(&b) enc.writeString(testCase.baseJSON) enc.AddSQLNullStringKeyOmitEmpty("foo", &testCase.sqlNullString) enc.Write() assert.Equal(t, testCase.expectedResult, b.String()) var b2 strings.Builder enc = NewEncoder(&b2) enc.writeString(testCase.baseJSON) enc.SQLNullStringKeyOmitEmpty("foo", &testCase.sqlNullString) enc.Write() assert.Equal(t, testCase.expectedResult, b2.String()) }) } }, ) } func TestAddSQLNullString(t *testing.T) { t.Run( "AddSQLNullString", func(t *testing.T) { testCases := []struct { name string sqlNullString sql.NullString baseJSON string expectedResult string err bool }{ { name: "it should encode a null string", sqlNullString: sql.NullString{ String: "foo bar", }, baseJSON: "[", expectedResult: `["foo bar"`, }, { name: "it should encode a null string", sqlNullString: sql.NullString{ String: "foo \t bar", }, baseJSON: "[", expectedResult: `["foo \t bar"`, }, { name: "it should encode a null string", sqlNullString: sql.NullString{ String: "foo \t bar", }, baseJSON: "[", expectedResult: `["foo \t bar"`, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { var b strings.Builder enc := NewEncoder(&b) enc.writeString(testCase.baseJSON) enc.AddSQLNullString(&testCase.sqlNullString) enc.Write() assert.Equal(t, testCase.expectedResult, b.String()) var b2 strings.Builder enc = NewEncoder(&b2) enc.writeString(testCase.baseJSON) enc.SQLNullString(&testCase.sqlNullString) enc.Write() assert.Equal(t, testCase.expectedResult, b2.String()) }) } }, ) t.Run( "AddSQLNullStringKeyOmitEmpty, is should encode a sql.NullString", func(t *testing.T) { testCases := []struct { name string sqlNullString sql.NullString baseJSON string expectedResult string err bool }{ { name: "it should encode a null string", sqlNullString: sql.NullString{ String: "foo bar", Valid: true, }, baseJSON: "[", expectedResult: `["foo bar"`, }, { name: "it should not encode anything as null string is invalid", sqlNullString: sql.NullString{ String: "foo \t bar", Valid: false, }, baseJSON: "[", expectedResult: `[`, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { var b strings.Builder enc := NewEncoder(&b) enc.writeString(testCase.baseJSON) enc.AddSQLNullStringOmitEmpty(&testCase.sqlNullString) enc.Write() assert.Equal(t, testCase.expectedResult, b.String()) var b2 strings.Builder enc = NewEncoder(&b2) enc.writeString(testCase.baseJSON) enc.SQLNullStringOmitEmpty(&testCase.sqlNullString) enc.Write() assert.Equal(t, testCase.expectedResult, b2.String()) }) } }, ) } // NullInt64 func TestEncoceSQLNullInt64(t *testing.T) { testCases := []struct { name string sqlNullInt64 sql.NullInt64 expectedResult string err bool }{ { name: "it should encode a null string", sqlNullInt64: sql.NullInt64{ Int64: int64(1), }, expectedResult: `1`, }, { name: "it should return an err as the string is invalid", sqlNullInt64: sql.NullInt64{ Int64: int64(2), }, expectedResult: `2`, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { var b strings.Builder enc := NewEncoder(&b) err := enc.EncodeSQLNullInt64(&testCase.sqlNullInt64) if testCase.err { assert.NotNil(t, err) } else { assert.Nil(t, err) assert.Equal(t, testCase.expectedResult, b.String()) } }) } t.Run( "should panic as the encoder is pooled", func(t *testing.T) { builder := &strings.Builder{} enc := NewEncoder(builder) enc.isPooled = 1 defer func() { err := recover() assert.NotNil(t, err, "err should not be nil") assert.IsType(t, InvalidUsagePooledEncoderError(""), err, "err should be of type InvalidUsagePooledEncoderError") }() _ = enc.EncodeSQLNullInt64(&sql.NullInt64{}) assert.True(t, false, "should not be called as encoder should have panicked") }, ) t.Run( "should return an error as the writer encounters an error", func(t *testing.T) { builder := TestWriterError("") enc := NewEncoder(builder) err := enc.EncodeSQLNullInt64(&sql.NullInt64{}) assert.NotNil(t, err) }, ) } func TestAddSQLNullInt64Key(t *testing.T) { t.Run( "AddSQLNullInt64Key", func(t *testing.T) { testCases := []struct { name string sqlNullInt64 sql.NullInt64 baseJSON string expectedResult string err bool }{ { name: "it should encode a null string", sqlNullInt64: sql.NullInt64{ Int64: 1, }, baseJSON: "{", expectedResult: `{"foo":1`, }, { name: "it should encode a null string", sqlNullInt64: sql.NullInt64{ Int64: 2, }, baseJSON: "{", expectedResult: `{"foo":2`, }, { name: "it should encode a null string", sqlNullInt64: sql.NullInt64{ Int64: 2, }, baseJSON: "{", expectedResult: `{"foo":2`, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { var b strings.Builder enc := NewEncoder(&b) enc.writeString(testCase.baseJSON) enc.AddSQLNullInt64Key("foo", &testCase.sqlNullInt64) enc.Write() assert.Equal(t, testCase.expectedResult, b.String()) var b2 strings.Builder enc = NewEncoder(&b2) enc.writeString(testCase.baseJSON) enc.SQLNullInt64Key("foo", &testCase.sqlNullInt64) enc.Write() assert.Equal(t, testCase.expectedResult, b2.String()) }) } }, ) t.Run( "AddSQLNullInt64KeyOmitEmpty, is should encode a sql.NullInt64", func(t *testing.T) { testCases := []struct { name string sqlNullInt64 sql.NullInt64 baseJSON string expectedResult string err bool }{ { name: "it should encode a null string", sqlNullInt64: sql.NullInt64{ Int64: 1, Valid: true, }, baseJSON: "{", expectedResult: `{"foo":1`, }, { name: "it should not encode anything as null string is invalid", sqlNullInt64: sql.NullInt64{ Int64: 2, Valid: false, }, baseJSON: "{", expectedResult: `{`, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { var b strings.Builder enc := NewEncoder(&b) enc.writeString(testCase.baseJSON) enc.AddSQLNullInt64KeyOmitEmpty("foo", &testCase.sqlNullInt64) enc.Write() assert.Equal(t, testCase.expectedResult, b.String()) var b2 strings.Builder enc = NewEncoder(&b2) enc.writeString(testCase.baseJSON) enc.SQLNullInt64KeyOmitEmpty("foo", &testCase.sqlNullInt64) enc.Write() assert.Equal(t, testCase.expectedResult, b2.String()) }) } }, ) } func TestAddSQLNullInt64(t *testing.T) { t.Run( "AddSQLNullInt64", func(t *testing.T) { testCases := []struct { name string sqlNullInt64 sql.NullInt64 baseJSON string expectedResult string err bool }{ { name: "it should encode a null string", sqlNullInt64: sql.NullInt64{ Int64: 1, }, baseJSON: "[", expectedResult: `[1`, }, { name: "it should encode a null string", sqlNullInt64: sql.NullInt64{ Int64: 2, }, baseJSON: "[", expectedResult: `[2`, }, { name: "it should encode a null string", sqlNullInt64: sql.NullInt64{ Int64: 2, }, baseJSON: "[", expectedResult: `[2`, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { var b strings.Builder enc := NewEncoder(&b) enc.writeString(testCase.baseJSON) enc.AddSQLNullInt64(&testCase.sqlNullInt64) enc.Write() assert.Equal(t, testCase.expectedResult, b.String()) var b2 strings.Builder enc = NewEncoder(&b2) enc.writeString(testCase.baseJSON) enc.SQLNullInt64(&testCase.sqlNullInt64) enc.Write() assert.Equal(t, testCase.expectedResult, b2.String()) }) } }, ) t.Run( "AddSQLNullInt64KeyOmitEmpty, is should encode a sql.NullInt64", func(t *testing.T) { testCases := []struct { name string sqlNullInt64 sql.NullInt64 baseJSON string expectedResult string err bool }{ { name: "it should encode a null string", sqlNullInt64: sql.NullInt64{ Int64: 2, Valid: true, }, baseJSON: "[", expectedResult: `[2`, }, { name: "it should not encode anything as null string is invalid", sqlNullInt64: sql.NullInt64{ Int64: 2, Valid: false, }, baseJSON: "[", expectedResult: `[`, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { var b strings.Builder enc := NewEncoder(&b) enc.writeString(testCase.baseJSON) enc.AddSQLNullInt64OmitEmpty(&testCase.sqlNullInt64) enc.Write() assert.Equal(t, testCase.expectedResult, b.String()) var b2 strings.Builder enc = NewEncoder(&b2) enc.writeString(testCase.baseJSON) enc.SQLNullInt64OmitEmpty(&testCase.sqlNullInt64) enc.Write() assert.Equal(t, testCase.expectedResult, b2.String()) }) } }, ) } // NullFloat64 func TestEncoceSQLNullFloat64(t *testing.T) { testCases := []struct { name string sqlNullFloat64 sql.NullFloat64 expectedResult string err bool }{ { name: "it should encode a null string", sqlNullFloat64: sql.NullFloat64{ Float64: float64(1), }, expectedResult: `1`, }, { name: "it should return an err as the string is invalid", sqlNullFloat64: sql.NullFloat64{ Float64: float64(2), }, expectedResult: `2`, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { var b strings.Builder enc := NewEncoder(&b) err := enc.EncodeSQLNullFloat64(&testCase.sqlNullFloat64) if testCase.err { assert.NotNil(t, err) } else { assert.Nil(t, err) assert.Equal(t, testCase.expectedResult, b.String()) } }) } t.Run( "should panic as the encoder is pooled", func(t *testing.T) { builder := &strings.Builder{} enc := NewEncoder(builder) enc.isPooled = 1 defer func() { err := recover() assert.NotNil(t, err, "err should not be nil") assert.IsType(t, InvalidUsagePooledEncoderError(""), err, "err should be of type InvalidUsagePooledEncoderError") }() _ = enc.EncodeSQLNullFloat64(&sql.NullFloat64{}) assert.True(t, false, "should not be called as encoder should have panicked") }, ) t.Run( "should return an error as the writer encounters an error", func(t *testing.T) { builder := TestWriterError("") enc := NewEncoder(builder) err := enc.EncodeSQLNullFloat64(&sql.NullFloat64{}) assert.NotNil(t, err) }, ) } func TestAddSQLNullFloat64Key(t *testing.T) { t.Run( "AddSQLNullFloat64Key", func(t *testing.T) { testCases := []struct { name string sqlNullFloat64 sql.NullFloat64 baseJSON string expectedResult string err bool }{ { name: "it should encode a null string", sqlNullFloat64: sql.NullFloat64{ Float64: 1, }, baseJSON: "{", expectedResult: `{"foo":1`, }, { name: "it should encode a null string", sqlNullFloat64: sql.NullFloat64{ Float64: 2, }, baseJSON: "{", expectedResult: `{"foo":2`, }, { name: "it should encode a null string", sqlNullFloat64: sql.NullFloat64{ Float64: 2, }, baseJSON: "{", expectedResult: `{"foo":2`, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { var b strings.Builder enc := NewEncoder(&b) enc.writeString(testCase.baseJSON) enc.AddSQLNullFloat64Key("foo", &testCase.sqlNullFloat64) enc.Write() assert.Equal(t, testCase.expectedResult, b.String()) var b2 strings.Builder enc = NewEncoder(&b2) enc.writeString(testCase.baseJSON) enc.SQLNullFloat64Key("foo", &testCase.sqlNullFloat64) enc.Write() assert.Equal(t, testCase.expectedResult, b2.String()) }) } }, ) t.Run( "AddSQLNullFloat64KeyOmitEmpty, is should encode a sql.NullFloat64", func(t *testing.T) { testCases := []struct { name string sqlNullFloat64 sql.NullFloat64 baseJSON string expectedResult string err bool }{ { name: "it should encode a null string", sqlNullFloat64: sql.NullFloat64{ Float64: 1, Valid: true, }, baseJSON: "{", expectedResult: `{"foo":1`, }, { name: "it should not encode anything as null string is invalid", sqlNullFloat64: sql.NullFloat64{ Float64: 2, Valid: false, }, baseJSON: "{", expectedResult: `{`, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { var b strings.Builder enc := NewEncoder(&b) enc.writeString(testCase.baseJSON) enc.AddSQLNullFloat64KeyOmitEmpty("foo", &testCase.sqlNullFloat64) enc.Write() assert.Equal(t, testCase.expectedResult, b.String()) var b2 strings.Builder enc = NewEncoder(&b2) enc.writeString(testCase.baseJSON) enc.SQLNullFloat64KeyOmitEmpty("foo", &testCase.sqlNullFloat64) enc.Write() assert.Equal(t, testCase.expectedResult, b2.String()) }) } }, ) } func TestAddSQLNullFloat64(t *testing.T) { t.Run( "AddSQLNullFloat64", func(t *testing.T) { testCases := []struct { name string sqlNullFloat64 sql.NullFloat64 baseJSON string expectedResult string err bool }{ { name: "it should encode a null string", sqlNullFloat64: sql.NullFloat64{ Float64: 1, }, baseJSON: "[", expectedResult: `[1`, }, { name: "it should encode a null string", sqlNullFloat64: sql.NullFloat64{ Float64: 2, }, baseJSON: "[", expectedResult: `[2`, }, { name: "it should encode a null string", sqlNullFloat64: sql.NullFloat64{ Float64: 2, }, baseJSON: "[", expectedResult: `[2`, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { var b strings.Builder enc := NewEncoder(&b) enc.writeString(testCase.baseJSON) enc.AddSQLNullFloat64(&testCase.sqlNullFloat64) enc.Write() assert.Equal(t, testCase.expectedResult, b.String()) var b2 strings.Builder enc = NewEncoder(&b2) enc.writeString(testCase.baseJSON) enc.SQLNullFloat64(&testCase.sqlNullFloat64) enc.Write() assert.Equal(t, testCase.expectedResult, b2.String()) }) } }, ) t.Run( "AddSQLNullFloat64KeyOmitEmpty, is should encode a sql.NullFloat64", func(t *testing.T) { testCases := []struct { name string sqlNullFloat64 sql.NullFloat64 baseJSON string expectedResult string err bool }{ { name: "it should encode a null string", sqlNullFloat64: sql.NullFloat64{ Float64: 2, Valid: true, }, baseJSON: "[", expectedResult: `[2`, }, { name: "it should not encode anything as null string is invalid", sqlNullFloat64: sql.NullFloat64{ Float64: 2, Valid: false, }, baseJSON: "[", expectedResult: `[`, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { var b strings.Builder enc := NewEncoder(&b) enc.writeString(testCase.baseJSON) enc.AddSQLNullFloat64OmitEmpty(&testCase.sqlNullFloat64) enc.Write() assert.Equal(t, testCase.expectedResult, b.String()) var b2 strings.Builder enc = NewEncoder(&b2) enc.writeString(testCase.baseJSON) enc.SQLNullFloat64OmitEmpty(&testCase.sqlNullFloat64) enc.Write() assert.Equal(t, testCase.expectedResult, b2.String()) }) } }, ) } // NullBool func TestEncoceSQLNullBool(t *testing.T) { testCases := []struct { name string sqlNullBool sql.NullBool expectedResult string err bool }{ { name: "it should encode a null string", sqlNullBool: sql.NullBool{ Bool: true, }, expectedResult: `true`, }, { name: "it should return an err as the string is invalid", sqlNullBool: sql.NullBool{ Bool: false, }, expectedResult: `false`, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { var b strings.Builder enc := NewEncoder(&b) err := enc.EncodeSQLNullBool(&testCase.sqlNullBool) if testCase.err { assert.NotNil(t, err) } else { assert.Nil(t, err) assert.Equal(t, testCase.expectedResult, b.String()) } }) } t.Run( "should panic as the encoder is pooled", func(t *testing.T) { builder := &strings.Builder{} enc := NewEncoder(builder) enc.isPooled = 1 defer func() { err := recover() assert.NotNil(t, err, "err should not be nil") assert.IsType(t, InvalidUsagePooledEncoderError(""), err, "err should be of type InvalidUsagePooledEncoderError") }() _ = enc.EncodeSQLNullBool(&sql.NullBool{}) assert.True(t, false, "should not be called as encoder should have panicked") }, ) t.Run( "should return an error as the writer encounters an error", func(t *testing.T) { builder := TestWriterError("") enc := NewEncoder(builder) err := enc.EncodeSQLNullBool(&sql.NullBool{}) assert.NotNil(t, err) }, ) } func TestAddSQLNullBoolKey(t *testing.T) { t.Run( "AddSQLNullBoolKey", func(t *testing.T) { testCases := []struct { name string sqlNullBool sql.NullBool baseJSON string expectedResult string err bool }{ { name: "it should encode a null string", sqlNullBool: sql.NullBool{ Bool: true, }, baseJSON: "{", expectedResult: `{"foo":true`, }, { name: "it should encode a null string", sqlNullBool: sql.NullBool{ Bool: false, }, baseJSON: "{", expectedResult: `{"foo":false`, }, { name: "it should encode a null string", sqlNullBool: sql.NullBool{ Bool: true, }, baseJSON: "{", expectedResult: `{"foo":true`, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { var b strings.Builder enc := NewEncoder(&b) enc.writeString(testCase.baseJSON) enc.AddSQLNullBoolKey("foo", &testCase.sqlNullBool) enc.Write() assert.Equal(t, testCase.expectedResult, b.String()) var b2 strings.Builder enc = NewEncoder(&b2) enc.writeString(testCase.baseJSON) enc.SQLNullBoolKey("foo", &testCase.sqlNullBool) enc.Write() assert.Equal(t, testCase.expectedResult, b2.String()) }) } }, ) t.Run( "AddSQLNullBoolKeyOmitEmpty, is should encode a sql.NullBool", func(t *testing.T) { testCases := []struct { name string sqlNullBool sql.NullBool baseJSON string expectedResult string err bool }{ { name: "it should encode a null string", sqlNullBool: sql.NullBool{ Bool: true, Valid: true, }, baseJSON: "{", expectedResult: `{"foo":true`, }, { name: "it should not encode anything as null string is invalid", sqlNullBool: sql.NullBool{ Bool: true, Valid: false, }, baseJSON: "{", expectedResult: `{`, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { var b strings.Builder enc := NewEncoder(&b) enc.writeString(testCase.baseJSON) enc.AddSQLNullBoolKeyOmitEmpty("foo", &testCase.sqlNullBool) enc.Write() assert.Equal(t, testCase.expectedResult, b.String()) var b2 strings.Builder enc = NewEncoder(&b2) enc.writeString(testCase.baseJSON) enc.SQLNullBoolKeyOmitEmpty("foo", &testCase.sqlNullBool) enc.Write() assert.Equal(t, testCase.expectedResult, b2.String()) }) } }, ) } func TestAddSQLNullBool(t *testing.T) { t.Run( "AddSQLNullBool", func(t *testing.T) { testCases := []struct { name string sqlNullBool sql.NullBool baseJSON string expectedResult string err bool }{ { name: "it should encode a null string", sqlNullBool: sql.NullBool{ Bool: true, }, baseJSON: "[", expectedResult: `[true`, }, { name: "it should encode a null string", sqlNullBool: sql.NullBool{ Bool: true, }, baseJSON: "[", expectedResult: `[true`, }, { name: "it should encode a null string", sqlNullBool: sql.NullBool{ Bool: false, }, baseJSON: "[", expectedResult: `[false`, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { var b strings.Builder enc := NewEncoder(&b) enc.writeString(testCase.baseJSON) enc.AddSQLNullBool(&testCase.sqlNullBool) enc.Write() assert.Equal(t, testCase.expectedResult, b.String()) var b2 strings.Builder enc = NewEncoder(&b2) enc.writeString(testCase.baseJSON) enc.SQLNullBool(&testCase.sqlNullBool) enc.Write() assert.Equal(t, testCase.expectedResult, b2.String()) }) } }, ) t.Run( "AddSQLNullBoolKeyOmitEmpty, is should encode a sql.NullBool", func(t *testing.T) { testCases := []struct { name string sqlNullBool sql.NullBool baseJSON string expectedResult string err bool }{ { name: "it should encode a null string", sqlNullBool: sql.NullBool{ Bool: true, Valid: true, }, baseJSON: "[", expectedResult: `[true`, }, { name: "it should not encode anything as null string is invalid", sqlNullBool: sql.NullBool{ Bool: true, Valid: false, }, baseJSON: "[", expectedResult: `[`, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { var b strings.Builder enc := NewEncoder(&b) enc.writeString(testCase.baseJSON) enc.AddSQLNullBoolOmitEmpty(&testCase.sqlNullBool) enc.Write() assert.Equal(t, testCase.expectedResult, b.String()) var b2 strings.Builder enc = NewEncoder(&b2) enc.writeString(testCase.baseJSON) enc.SQLNullBoolOmitEmpty(&testCase.sqlNullBool) enc.Write() assert.Equal(t, testCase.expectedResult, b2.String()) }) } }, ) } func TestEncoderSQLNullStringEmpty(t *testing.T) { var testCases = []struct { name string baseJSON string expectedJSON string }{ { name: "basic 1st elem", baseJSON: "[", expectedJSON: `[null,"bar"`, }, { name: "basic 2nd elem", baseJSON: `["test"`, expectedJSON: `["test",null,"bar"`, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { var b strings.Builder var enc = NewEncoder(&b) enc.writeString(testCase.baseJSON) enc.AddSQLNullStringNullEmpty(&sql.NullString{"", true}) enc.SQLNullStringNullEmpty(&sql.NullString{"bar", true}) enc.Write() assert.Equal(t, testCase.expectedJSON, b.String()) }) } } func TestEncoderSQLNullStringKeyNullEmpty(t *testing.T) { var testCases = []struct { name string baseJSON string expectedJSON string }{ { name: "basic 1st elem", baseJSON: "{", expectedJSON: `{"foo":null,"bar":"bar"`, }, { name: "basic 2nd elem", baseJSON: `{"test":"test"`, expectedJSON: `{"test":"test","foo":null,"bar":"bar"`, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { var b strings.Builder var enc = NewEncoder(&b) enc.writeString(testCase.baseJSON) enc.SQLNullStringKeyNullEmpty("foo", &sql.NullString{"", true}) enc.SQLNullStringKeyNullEmpty("bar", &sql.NullString{"bar", true}) enc.Write() assert.Equal(t, testCase.expectedJSON, b.String()) }) } } func TestEncoderSQLNullBoolEmpty(t *testing.T) { var testCases = []struct { name string baseJSON string expectedJSON string }{ { name: "basic 1st elem", baseJSON: "[", expectedJSON: `[null,true`, }, { name: "basic 2nd elem", baseJSON: `["test"`, expectedJSON: `["test",null,true`, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { var b strings.Builder var enc = NewEncoder(&b) enc.writeString(testCase.baseJSON) enc.SQLNullBoolNullEmpty(&sql.NullBool{false, true}) enc.SQLNullBoolNullEmpty(&sql.NullBool{true, true}) enc.Write() assert.Equal(t, testCase.expectedJSON, b.String()) }) } } func TestEncoderSQLNullBoolKeyNullEmpty(t *testing.T) { var testCases = []struct { name string baseJSON string expectedJSON string }{ { name: "basic 1st elem", baseJSON: "{", expectedJSON: `{"foo":null,"bar":true`, }, { name: "basic 2nd elem", baseJSON: `{"test":"test"`, expectedJSON: `{"test":"test","foo":null,"bar":true`, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { var b strings.Builder var enc = NewEncoder(&b) enc.writeString(testCase.baseJSON) enc.AddSQLNullBoolKeyNullEmpty("foo", &sql.NullBool{false, true}) enc.SQLNullBoolKeyNullEmpty("bar", &sql.NullBool{true, true}) enc.Write() assert.Equal(t, testCase.expectedJSON, b.String()) }) } } func TestEncoderSQLNullInt64Empty(t *testing.T) { var testCases = []struct { name string baseJSON string expectedJSON string }{ { name: "basic 1st elem", baseJSON: "[", expectedJSON: `[null,1`, }, { name: "basic 2nd elem", baseJSON: `["test"`, expectedJSON: `["test",null,1`, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { var b strings.Builder var enc = NewEncoder(&b) enc.writeString(testCase.baseJSON) enc.AddSQLNullInt64NullEmpty(&sql.NullInt64{0, true}) enc.SQLNullInt64NullEmpty(&sql.NullInt64{1, true}) enc.Write() assert.Equal(t, testCase.expectedJSON, b.String()) }) } } func TestEncoderSQLNullInt64KeyNullEmpty(t *testing.T) { var testCases = []struct { name string baseJSON string expectedJSON string }{ { name: "basic 1st elem", baseJSON: "{", expectedJSON: `{"foo":null,"bar":1`, }, { name: "basic 2nd elem", baseJSON: `{"test":"test"`, expectedJSON: `{"test":"test","foo":null,"bar":1`, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { var b strings.Builder var enc = NewEncoder(&b) enc.writeString(testCase.baseJSON) enc.AddSQLNullInt64KeyNullEmpty("foo", &sql.NullInt64{0, true}) enc.SQLNullInt64KeyNullEmpty("bar", &sql.NullInt64{1, true}) enc.Write() assert.Equal(t, testCase.expectedJSON, b.String()) }) } } func TestEncoderSQLNullFloat64Empty(t *testing.T) { var testCases = []struct { name string baseJSON string expectedJSON string }{ { name: "basic 1st elem", baseJSON: "[", expectedJSON: `[null,1`, }, { name: "basic 2nd elem", baseJSON: `["test"`, expectedJSON: `["test",null,1`, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { var b strings.Builder var enc = NewEncoder(&b) enc.writeString(testCase.baseJSON) enc.AddSQLNullFloat64NullEmpty(&sql.NullFloat64{0, true}) enc.SQLNullFloat64NullEmpty(&sql.NullFloat64{1, true}) enc.Write() assert.Equal(t, testCase.expectedJSON, b.String()) }) } } func TestEncoderSQLNullFloat64KeyNullEmpty(t *testing.T) { var testCases = []struct { name string baseJSON string expectedJSON string }{ { name: "basic 1st elem", baseJSON: "{", expectedJSON: `{"foo":null,"bar":1`, }, { name: "basic 2nd elem", baseJSON: `{"test":"test"`, expectedJSON: `{"test":"test","foo":null,"bar":1`, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { var b strings.Builder var enc = NewEncoder(&b) enc.writeString(testCase.baseJSON) enc.AddSQLNullFloat64KeyNullEmpty("foo", &sql.NullFloat64{0, true}) enc.SQLNullFloat64KeyNullEmpty("bar", &sql.NullFloat64{1, true}) enc.Write() assert.Equal(t, testCase.expectedJSON, b.String()) }) } } golang-github-francoispqt-gojay-1.2.13/encode_stream.go000066400000000000000000000120121365331665100231270ustar00rootroot00000000000000package gojay import ( "strconv" "sync" "time" ) // MarshalerStream is the interface to implement // to continuously encode of stream of data. type MarshalerStream interface { MarshalStream(enc *StreamEncoder) } // A StreamEncoder reads and encodes values to JSON from an input stream. // // It implements conext.Context and provide a channel to notify interruption. type StreamEncoder struct { mux *sync.RWMutex *Encoder nConsumer int delimiter byte deadline *time.Time done chan struct{} } // EncodeStream spins up a defined number of non blocking consumers of the MarshalerStream m. // // m must implement MarshalerStream. Ideally m is a channel. See example for implementation. // // See the documentation for Marshal for details about the conversion of Go value to JSON. func (s *StreamEncoder) EncodeStream(m MarshalerStream) { // if a single consumer, just use this encoder if s.nConsumer == 1 { go consume(s, s, m) return } // else use this Encoder only for first consumer // and use new encoders for other consumers // this is to avoid concurrent writing to same buffer // resulting in a weird JSON go consume(s, s, m) for i := 1; i < s.nConsumer; i++ { s.mux.RLock() select { case <-s.done: default: ss := Stream.borrowEncoder(s.w) ss.mux.Lock() ss.done = s.done ss.buf = make([]byte, 0, 512) ss.delimiter = s.delimiter go consume(s, ss, m) ss.mux.Unlock() } s.mux.RUnlock() } return } // LineDelimited sets the delimiter to a new line character. // // It will add a new line after each JSON marshaled by the MarshalerStream func (s *StreamEncoder) LineDelimited() *StreamEncoder { s.delimiter = '\n' return s } // CommaDelimited sets the delimiter to a comma. // // It will add a new line after each JSON marshaled by the MarshalerStream func (s *StreamEncoder) CommaDelimited() *StreamEncoder { s.delimiter = ',' return s } // NConsumer sets the number of non blocking go routine to consume the stream. func (s *StreamEncoder) NConsumer(n int) *StreamEncoder { s.nConsumer = n return s } // Release sends back a Decoder to the pool. // If a decoder is used after calling Release // a panic will be raised with an InvalidUsagePooledDecoderError error. func (s *StreamEncoder) Release() { s.isPooled = 1 streamEncPool.Put(s) } // Done returns a channel that's closed when work is done. // It implements context.Context func (s *StreamEncoder) Done() <-chan struct{} { return s.done } // Err returns nil if Done is not yet closed. // If Done is closed, Err returns a non-nil error explaining why. // It implements context.Context func (s *StreamEncoder) Err() error { return s.err } // Deadline returns the time when work done on behalf of this context // should be canceled. Deadline returns ok==false when no deadline is // set. Successive calls to Deadline return the same results. func (s *StreamEncoder) Deadline() (time.Time, bool) { if s.deadline != nil { return *s.deadline, true } return time.Time{}, false } // SetDeadline sets the deadline func (s *StreamEncoder) SetDeadline(t time.Time) { s.deadline = &t } // Value implements context.Context func (s *StreamEncoder) Value(key interface{}) interface{} { return nil } // Cancel cancels the consumers of the stream, interrupting the stream encoding. // // After calling cancel, Done() will return a closed channel. func (s *StreamEncoder) Cancel(err error) { s.mux.Lock() defer s.mux.Unlock() select { case <-s.done: default: s.err = err close(s.done) } } // AddObject adds an object to be encoded. // value must implement MarshalerJSONObject. func (s *StreamEncoder) AddObject(v MarshalerJSONObject) { if v.IsNil() { return } s.Encoder.writeByte('{') v.MarshalJSONObject(s.Encoder) s.Encoder.writeByte('}') s.Encoder.writeByte(s.delimiter) } // AddString adds a string to be encoded. func (s *StreamEncoder) AddString(v string) { s.Encoder.writeByte('"') s.Encoder.writeString(v) s.Encoder.writeByte('"') s.Encoder.writeByte(s.delimiter) } // AddArray adds an implementation of MarshalerJSONArray to be encoded. func (s *StreamEncoder) AddArray(v MarshalerJSONArray) { s.Encoder.writeByte('[') v.MarshalJSONArray(s.Encoder) s.Encoder.writeByte(']') s.Encoder.writeByte(s.delimiter) } // AddInt adds an int to be encoded. func (s *StreamEncoder) AddInt(value int) { s.buf = strconv.AppendInt(s.buf, int64(value), 10) s.Encoder.writeByte(s.delimiter) } // AddFloat64 adds a float64 to be encoded. func (s *StreamEncoder) AddFloat64(value float64) { s.buf = strconv.AppendFloat(s.buf, value, 'f', -1, 64) s.Encoder.writeByte(s.delimiter) } // AddFloat adds a float64 to be encoded. func (s *StreamEncoder) AddFloat(value float64) { s.AddFloat64(value) } // Non exposed func consume(init *StreamEncoder, s *StreamEncoder, m MarshalerStream) { defer s.Release() for { select { case <-init.Done(): return default: m.MarshalStream(s) if s.Encoder.err != nil { init.Cancel(s.Encoder.err) return } i, err := s.Encoder.Write() if err != nil || i == 0 { init.Cancel(err) return } } } } golang-github-francoispqt-gojay-1.2.13/encode_stream_pool.go000066400000000000000000000021551365331665100241670ustar00rootroot00000000000000package gojay import ( "io" "sync" ) // NewEncoder returns a new StreamEncoder. // It takes an io.Writer implementation to output data. // It initiates the done channel returned by Done(). func (s stream) NewEncoder(w io.Writer) *StreamEncoder { enc := BorrowEncoder(w) return &StreamEncoder{Encoder: enc, nConsumer: 1, done: make(chan struct{}, 1), mux: &sync.RWMutex{}} } // BorrowEncoder borrows a StreamEncoder from the pool. // It takes an io.Writer implementation to output data. // It initiates the done channel returned by Done(). // // If no StreamEncoder is available in the pool, it returns a fresh one func (s stream) BorrowEncoder(w io.Writer) *StreamEncoder { streamEnc := streamEncPool.Get().(*StreamEncoder) streamEnc.w = w streamEnc.Encoder.err = nil streamEnc.done = make(chan struct{}, 1) streamEnc.Encoder.buf = streamEnc.buf[:0] streamEnc.nConsumer = 1 streamEnc.isPooled = 0 return streamEnc } func (s stream) borrowEncoder(w io.Writer) *StreamEncoder { streamEnc := streamEncPool.Get().(*StreamEncoder) streamEnc.isPooled = 0 streamEnc.w = w streamEnc.Encoder.err = nil return streamEnc } golang-github-francoispqt-gojay-1.2.13/encode_stream_test.go000066400000000000000000000220501365331665100241710ustar00rootroot00000000000000package gojay import ( "os" "sync" "testing" "time" "github.com/stretchr/testify/assert" ) type StreamChanObject chan *testObject func (s StreamChanObject) MarshalStream(enc *StreamEncoder) { select { case <-enc.Done(): return case o := <-s: enc.AddObject(o) } } type StreamChanSlice chan *TestEncodingArrStrings func (s StreamChanSlice) MarshalStream(enc *StreamEncoder) { select { case <-enc.Done(): return case o := <-s: enc.AddArray(o) } } type StreamChanString chan string func (s StreamChanString) MarshalStream(enc *StreamEncoder) { select { case <-enc.Done(): return case o := <-s: enc.AddString(o) } } type StreamChanInt chan int func (s StreamChanInt) MarshalStream(enc *StreamEncoder) { select { case <-enc.Done(): return case o := <-s: enc.AddInt(o) } } type StreamChanFloat chan float64 func (s StreamChanFloat) MarshalStream(enc *StreamEncoder) { select { case <-enc.Done(): return case o := <-s: enc.AddFloat(o) } } type StreamChanError chan *testObject func (s StreamChanError) MarshalStream(enc *StreamEncoder) { select { case <-enc.Done(): return case <-s: enc.AddInterface(struct{}{}) } } // TestWriter to assert result type TestWriter struct { nWrite *int target int enc *StreamEncoder result [][]byte mux *sync.RWMutex } func (w *TestWriter) Write(b []byte) (int, error) { if len(b) > 0 { w.mux.Lock() w.result = append(w.result, b) if len(w.result) == w.target { w.enc.Cancel(nil) } w.mux.Unlock() } return len(b), nil } func feedStreamNil(s chan *testObject, target int) { for i := 0; i < target; i++ { s <- nil } } func feedStream(s chan *testObject, target int) { for i := 0; i < target; i++ { s <- &testObject{} } } func feedStreamSlices(s chan *TestEncodingArrStrings, target int) { for i := 0; i < target; i++ { s <- &TestEncodingArrStrings{"test", "test2"} } } func feedStreamStrings(s chan string, target int) { for i := 0; i < target; i++ { s <- "hello" } } func feedStreamInt(s chan int, target int) { for i := 0; i < target; i++ { s <- i } } func feedStreamFloat(s chan float64, target int) { for i := 0; i < target; i++ { s <- float64(i) } } func TestEncodeStream(t *testing.T) { t.Run("single-consumer-object", func(t *testing.T) { expectedStr := `{"testStr":"","testInt":0,"testInt64":0,"testInt32":0,"testInt16":0,"testInt8":0,"testUint64":0,"testUint32":0,"testUint16":0,"testUint8":0,"testFloat64":0,"testFloat32":0,"testBool":false} ` // create our writer w := &TestWriter{target: 100, mux: &sync.RWMutex{}} enc := Stream.NewEncoder(w).LineDelimited() w.enc = enc s := StreamChanObject(make(chan *testObject)) go enc.EncodeStream(s) go feedStream(s, 100) select { case <-enc.Done(): assert.Nil(t, enc.Err(), "enc.Err() should be nil") assert.Len(t, w.result, 100, "w.result should be 100") for _, b := range w.result { assert.Equal(t, expectedStr, string(b), "every byte buffer should be equal to expected string") } } }) t.Run("single-consumer-slice", func(t *testing.T) { expectedStr := `["test","test2"] ` // create our writer w := &TestWriter{target: 100, mux: &sync.RWMutex{}} enc := Stream.NewEncoder(w).LineDelimited() w.enc = enc s := StreamChanSlice(make(chan *TestEncodingArrStrings)) go enc.EncodeStream(s) go feedStreamSlices(s, 100) select { case <-enc.Done(): assert.Nil(t, enc.Err(), "enc.Err() should be nil") assert.Len(t, w.result, 100, "w.result should be 100") for _, b := range w.result { assert.Equal(t, expectedStr, string(b), "every byte buffer should be equal to expected string") } } }) t.Run("single-consumer-string", func(t *testing.T) { expectedStr := `"hello" ` // create our writer w := &TestWriter{target: 100, mux: &sync.RWMutex{}} enc := Stream.NewEncoder(w).LineDelimited() w.enc = enc s := StreamChanString(make(chan string)) go enc.EncodeStream(s) go feedStreamStrings(s, 100) select { case <-enc.Done(): assert.Nil(t, enc.Err(), "enc.Err() should be nil") assert.Len(t, w.result, 100, "w.result should be 100") for _, b := range w.result { assert.Equal(t, expectedStr, string(b), "every byte buffer should be equal to expected string") } } }) t.Run("single-consumer-object-nil-value", func(t *testing.T) { expectedStr := `` // create our writer w := &TestWriter{target: 100, mux: &sync.RWMutex{}} enc := Stream.NewEncoder(w).LineDelimited() w.enc = enc s := StreamChanObject(make(chan *testObject)) go enc.EncodeStream(s) go feedStreamNil(s, 100) select { case <-enc.Done(): assert.Nil(t, enc.Err(), "enc.Err() should be nil") assert.Nil(t, enc.Err(), "enc.Err() should not be nil") for _, b := range w.result { assert.Equal(t, expectedStr, string(b), "every byte buffer should be equal to expected string") } } }) t.Run("single-consumer-int", func(t *testing.T) { // create our writer w := &TestWriter{target: 100, mux: &sync.RWMutex{}} enc := Stream.NewEncoder(w).LineDelimited() w.enc = enc s := StreamChanInt(make(chan int)) go enc.EncodeStream(s) go feedStreamInt(s, 100) select { case <-enc.Done(): assert.Nil(t, enc.Err(), "enc.Err() should be nil") assert.Len(t, w.result, 100, "w.result should be 100") } }) t.Run("single-consumer-float", func(t *testing.T) { // create our writer w := &TestWriter{target: 100, mux: &sync.RWMutex{}} enc := Stream.NewEncoder(w).LineDelimited() w.enc = enc s := StreamChanFloat(make(chan float64)) go enc.EncodeStream(s) go feedStreamFloat(s, 100) select { case <-enc.Done(): assert.Nil(t, enc.Err(), "enc.Err() should be nil") assert.Len(t, w.result, 100, "w.result should be 100") } }) t.Run("single-consumer-marshal-error", func(t *testing.T) { // create our writer w := &TestWriter{target: 100, mux: &sync.RWMutex{}} enc := Stream.NewEncoder(w).LineDelimited() w.enc = enc s := StreamChanError(make(chan *testObject)) go enc.EncodeStream(s) go feedStream(s, 100) select { case <-enc.Done(): assert.NotNil(t, enc.Err(), "enc.Err() should not be nil") } }) t.Run("single-consumer-write-error", func(t *testing.T) { // create our writer w := TestWriterError("") enc := Stream.NewEncoder(w).LineDelimited() s := StreamChanObject(make(chan *testObject)) go enc.EncodeStream(s) go feedStream(s, 100) select { case <-enc.Done(): assert.NotNil(t, enc.Err(), "enc.Err() should not be nil") } }) t.Run("multiple-consumer-object-comma-delimited", func(t *testing.T) { expectedStr := `{"testStr":"","testInt":0,"testInt64":0,"testInt32":0,"testInt16":0,"testInt8":0,"testUint64":0,"testUint32":0,"testUint16":0,"testUint8":0,"testFloat64":0,"testFloat32":0,"testBool":false},` // create our writer w := &TestWriter{target: 5000, mux: &sync.RWMutex{}} enc := Stream.BorrowEncoder(w).NConsumer(50).CommaDelimited() w.enc = enc s := StreamChanObject(make(chan *testObject)) go enc.EncodeStream(s) go feedStream(s, 5000) select { case <-enc.Done(): assert.Nil(t, enc.Err(), "enc.Err() should be nil") assert.Len(t, w.result, 5000, "w.result should be 100") for _, b := range w.result { assert.Equal(t, expectedStr, string(b), "every byte buffer should be equal to expected string") } } }) t.Run("multiple-consumer-object-line-delimited", func(t *testing.T) { expectedStr := `{"testStr":"","testInt":0,"testInt64":0,"testInt32":0,"testInt16":0,"testInt8":0,"testUint64":0,"testUint32":0,"testUint16":0,"testUint8":0,"testFloat64":0,"testFloat32":0,"testBool":false} ` // create our writer w := &TestWriter{target: 5000, mux: &sync.RWMutex{}} enc := Stream.NewEncoder(w).NConsumer(50).LineDelimited() w.enc = enc s := StreamChanObject(make(chan *testObject)) go feedStream(s, 5000) go enc.EncodeStream(s) select { case <-enc.Done(): assert.Nil(t, enc.Err(), "enc.Err() should be nil") assert.Len(t, w.result, 5000, "w.result should be 100") for _, b := range w.result { assert.Equal(t, expectedStr, string(b), "every byte buffer should be equal to expected string") } } }) t.Run("multiple-consumer-object-chan-closed", func(t *testing.T) { // create our writer w := &TestWriter{target: 5000, mux: &sync.RWMutex{}} enc := Stream.NewEncoder(w).NConsumer(50).LineDelimited() w.enc = enc s := StreamChanObject(make(chan *testObject)) close(enc.done) go feedStream(s, 5000) go enc.EncodeStream(s) select { case <-enc.Done(): assert.Nil(t, enc.Err(), "enc.Err() should be nil") assert.Len(t, w.result, 0, "w.result should be 0") } }) t.Run("encoder-deadline", func(t *testing.T) { enc := Stream.NewEncoder(os.Stdout) now := time.Now() enc.SetDeadline(now) d, _ := enc.Deadline() assert.Equal(t, now, d, "deadline should be the one just set") }) t.Run("encoder-deadline-unset", func(t *testing.T) { enc := Stream.NewEncoder(os.Stdout) d, _ := enc.Deadline() assert.Equal(t, time.Time{}, d, "deadline should be the one just set") }) // just for coverage t.Run("encoder-context-value", func(t *testing.T) { enc := Stream.NewEncoder(os.Stdout) assert.Nil(t, enc.Value(""), "enc.Value should be nil") }) } golang-github-francoispqt-gojay-1.2.13/encode_string.go000066400000000000000000000111721365331665100231500ustar00rootroot00000000000000package gojay // EncodeString encodes a string to func (enc *Encoder) EncodeString(s string) error { if enc.isPooled == 1 { panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder")) } _, _ = enc.encodeString(s) _, err := enc.Write() if err != nil { enc.err = err return err } return nil } // encodeString encodes a string to func (enc *Encoder) encodeString(v string) ([]byte, error) { enc.writeByte('"') enc.writeStringEscape(v) enc.writeByte('"') return enc.buf, nil } // AppendString appends a string to the buffer func (enc *Encoder) AppendString(v string) { enc.grow(len(v) + 2) enc.writeByte('"') enc.writeStringEscape(v) enc.writeByte('"') } // AddString adds a string to be encoded, must be used inside a slice or array encoding (does not encode a key) func (enc *Encoder) AddString(v string) { enc.String(v) } // AddStringOmitEmpty adds a string to be encoded or skips it if it is zero value. // Must be used inside a slice or array encoding (does not encode a key) func (enc *Encoder) AddStringOmitEmpty(v string) { enc.StringOmitEmpty(v) } // AddStringNullEmpty adds a string to be encoded or skips it if it is zero value. // Must be used inside a slice or array encoding (does not encode a key) func (enc *Encoder) AddStringNullEmpty(v string) { enc.StringNullEmpty(v) } // AddStringKey adds a string to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) AddStringKey(key, v string) { enc.StringKey(key, v) } // AddStringKeyOmitEmpty adds a string to be encoded or skips it if it is zero value. // Must be used inside an object as it will encode a key func (enc *Encoder) AddStringKeyOmitEmpty(key, v string) { enc.StringKeyOmitEmpty(key, v) } // AddStringKeyNullEmpty adds a string to be encoded or skips it if it is zero value. // Must be used inside an object as it will encode a key func (enc *Encoder) AddStringKeyNullEmpty(key, v string) { enc.StringKeyNullEmpty(key, v) } // String adds a string to be encoded, must be used inside a slice or array encoding (does not encode a key) func (enc *Encoder) String(v string) { enc.grow(len(v) + 4) r := enc.getPreviousRune() if r != '[' { enc.writeTwoBytes(',', '"') } else { enc.writeByte('"') } enc.writeStringEscape(v) enc.writeByte('"') } // StringOmitEmpty adds a string to be encoded or skips it if it is zero value. // Must be used inside a slice or array encoding (does not encode a key) func (enc *Encoder) StringOmitEmpty(v string) { if v == "" { return } r := enc.getPreviousRune() if r != '[' { enc.writeTwoBytes(',', '"') } else { enc.writeByte('"') } enc.writeStringEscape(v) enc.writeByte('"') } // StringNullEmpty adds a string to be encoded or skips it if it is zero value. // Must be used inside a slice or array encoding (does not encode a key) func (enc *Encoder) StringNullEmpty(v string) { r := enc.getPreviousRune() if v == "" { if r != '[' { enc.writeByte(',') enc.writeBytes(nullBytes) } else { enc.writeBytes(nullBytes) } return } if r != '[' { enc.writeTwoBytes(',', '"') } else { enc.writeByte('"') } enc.writeStringEscape(v) enc.writeByte('"') } // StringKey adds a string to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) StringKey(key, v string) { if enc.hasKeys { if !enc.keyExists(key) { return } } enc.grow(len(key) + len(v) + 5) r := enc.getPreviousRune() if r != '{' { enc.writeTwoBytes(',', '"') } else { enc.writeByte('"') } enc.writeStringEscape(key) enc.writeBytes(objKeyStr) enc.writeStringEscape(v) enc.writeByte('"') } // StringKeyOmitEmpty adds a string to be encoded or skips it if it is zero value. // Must be used inside an object as it will encode a key func (enc *Encoder) StringKeyOmitEmpty(key, v string) { if enc.hasKeys { if !enc.keyExists(key) { return } } if v == "" { return } enc.grow(len(key) + len(v) + 5) r := enc.getPreviousRune() if r != '{' { enc.writeTwoBytes(',', '"') } else { enc.writeByte('"') } enc.writeStringEscape(key) enc.writeBytes(objKeyStr) enc.writeStringEscape(v) enc.writeByte('"') } // StringKeyNullEmpty adds a string to be encoded or skips it if it is zero value. // Must be used inside an object as it will encode a key func (enc *Encoder) StringKeyNullEmpty(key, v string) { if enc.hasKeys { if !enc.keyExists(key) { return } } enc.grow(len(key) + len(v) + 5) r := enc.getPreviousRune() if r != '{' { enc.writeTwoBytes(',', '"') } else { enc.writeByte('"') } enc.writeStringEscape(key) enc.writeBytes(objKey) if v == "" { enc.writeBytes(nullBytes) return } enc.writeByte('"') enc.writeStringEscape(v) enc.writeByte('"') } golang-github-francoispqt-gojay-1.2.13/encode_string_test.go000066400000000000000000000151551365331665100242140ustar00rootroot00000000000000package gojay import ( "strings" "testing" "github.com/stretchr/testify/assert" ) func TestEncoderStringEncodeAPI(t *testing.T) { t.Run("basic", func(t *testing.T) { builder := &strings.Builder{} enc := NewEncoder(builder) err := enc.EncodeString("hello world") assert.Nil(t, err, "Error should be nil") assert.Equal( t, `"hello world"`, builder.String(), "Result of marshalling is different as the one expected") }) t.Run("utf8", func(t *testing.T) { builder := &strings.Builder{} enc := NewEncoder(builder) err := enc.EncodeString("漢字𩸽") assert.Nil(t, err, "Error should be nil") assert.Equal( t, `"漢字𩸽"`, builder.String(), "Result of marshalling is different as the one expected") }) t.Run("utf8-multibyte", func(t *testing.T) { str := "テュールスト マーティン ヤコブ ðŸ˜" builder := &strings.Builder{} enc := NewEncoder(builder) err := enc.EncodeString(str) assert.Nil(t, err, "Error should be nil") assert.Equal( t, `"テュールスト マーティン ヤコブ ðŸ˜"`, builder.String(), "Result of marshalling is different as the one expected") }) t.Run("escaped-sequence1", func(t *testing.T) { str := `テュールスト マ\ーテ ィン ヤコブ ðŸ˜` builder := &strings.Builder{} enc := NewEncoder(builder) err := enc.EncodeString(str) assert.Nil(t, err, "Error should be nil") assert.Equal( t, `"テュールスト マ\\ーテ\nィン ヤコブ ðŸ˜"`, builder.String(), "Result of marshalling is different as the one expected") }) t.Run("escaped-sequence2", func(t *testing.T) { str := `テュールスト マ\ーテ ィン ヤコブ 😠` builder := &strings.Builder{} enc := NewEncoder(builder) err := enc.EncodeString(str) assert.Nil(t, err, "Error should be nil") assert.Equal( t, `"テュールスト マ\\ーテ\nィン ヤコブ ðŸ˜\t"`, builder.String(), "Result of marshalling is different as the one expected") }) t.Run("escaped-sequence3", func(t *testing.T) { str := "hello \r world ð„ž" builder := &strings.Builder{} enc := NewEncoder(builder) err := enc.EncodeString(str) assert.Nil(t, err, "Error should be nil") assert.Equal( t, `"hello \r world ð„ž"`, builder.String(), "Result of marshalling is different as the one expected") }) t.Run("escaped-sequence3", func(t *testing.T) { str := "hello \b world ð„ž" builder := &strings.Builder{} enc := NewEncoder(builder) err := enc.EncodeString(str) assert.Nil(t, err, "Error should be nil") assert.Equal( t, `"hello \b world ð„ž"`, builder.String(), "Result of marshalling is different as the one expected") }) t.Run("escaped-control-char", func(t *testing.T) { str := "\u001b" builder := &strings.Builder{} enc := NewEncoder(builder) err := enc.EncodeString(str) assert.Nil(t, err, "Error should be nil") assert.Equal( t, `"\u001b"`, builder.String(), "Result of marshalling is different as the one expected") }) t.Run("escaped-sequence3", func(t *testing.T) { str := "hello \f world ð„ž" builder := &strings.Builder{} enc := NewEncoder(builder) err := enc.EncodeString(str) assert.Nil(t, err, "Error should be nil") assert.Equal( t, "\"hello \\f world ð„ž\"", builder.String(), "Result of marshalling is different as the one expected") }) } func TestEncoderStringEncodeAPIErrors(t *testing.T) { t.Run("pool-error", func(t *testing.T) { v := "" enc := BorrowEncoder(nil) enc.isPooled = 1 defer func() { err := recover() assert.NotNil(t, err, "err shouldnt be nil") assert.IsType(t, InvalidUsagePooledEncoderError(""), err, "err should be of type InvalidUsagePooledEncoderError") assert.Equal(t, "Invalid usage of pooled encoder", err.(InvalidUsagePooledEncoderError).Error(), "err should be of type InvalidUsagePooledDecoderError") }() _ = enc.EncodeString(v) assert.True(t, false, "should not be called as it should have panicked") }) t.Run("write-error", func(t *testing.T) { v := "test" w := TestWriterError("") enc := BorrowEncoder(w) defer enc.Release() err := enc.EncodeString(v) assert.NotNil(t, err, "err should not be nil") }) } func TestEncoderStringMarshalAPI(t *testing.T) { t.Run("basic", func(t *testing.T) { r, err := Marshal("string") assert.Nil(t, err, "Error should be nil") assert.Equal( t, `"string"`, string(r), "Result of marshalling is different as the one expected") }) t.Run("utf8", func(t *testing.T) { r, err := Marshal("漢字") assert.Nil(t, err, "Error should be nil") assert.Equal( t, `"漢字"`, string(r), "Result of marshalling is different as the one expected") }) } func TestEncoderStringNullEmpty(t *testing.T) { var testCases = []struct { name string baseJSON string expectedJSON string }{ { name: "basic 1st elem", baseJSON: "[", expectedJSON: `[null,"true"`, }, { name: "basic 2nd elem", baseJSON: `["test"`, expectedJSON: `["test",null,"true"`, }, { name: "basic 2nd elem", baseJSON: `["test"`, expectedJSON: `["test",null,"true"`, }, } for _, testCase := range testCases { t.Run("true", func(t *testing.T) { var b strings.Builder var enc = NewEncoder(&b) enc.writeString(testCase.baseJSON) enc.StringNullEmpty("") enc.AddStringNullEmpty("true") enc.Write() assert.Equal(t, testCase.expectedJSON, b.String()) }) } } func TestEncoderStringNullEmpty2(t *testing.T) { var testCases = []struct { name string baseJSON string expectedJSON string }{ { name: "basic 1st elem", baseJSON: "[", expectedJSON: `["test"`, }, } for _, testCase := range testCases { t.Run("true", func(t *testing.T) { var b strings.Builder var enc = NewEncoder(&b) enc.writeString(testCase.baseJSON) enc.StringNullEmpty("test") enc.Write() assert.Equal(t, testCase.expectedJSON, b.String()) }) } } func TestEncoderStringNullKeyEmpty(t *testing.T) { var testCases = []struct { name string baseJSON string expectedJSON string }{ { name: "basic 1st elem", baseJSON: "{", expectedJSON: `{"foo":null,"bar":"true"`, }, { name: "basic 2nd elem", baseJSON: `{"test":"test"`, expectedJSON: `{"test":"test","foo":null,"bar":"true"`, }, } for _, testCase := range testCases { t.Run("true", func(t *testing.T) { var b strings.Builder var enc = NewEncoder(&b) enc.writeString(testCase.baseJSON) enc.StringKeyNullEmpty("foo", "") enc.AddStringKeyNullEmpty("bar", "true") enc.Write() assert.Equal(t, testCase.expectedJSON, b.String()) }) } } golang-github-francoispqt-gojay-1.2.13/encode_test.go000066400000000000000000000016731365331665100226260ustar00rootroot00000000000000package gojay import ( "errors" "testing" "github.com/stretchr/testify/assert" ) type TestWriterError string func (t TestWriterError) Write(b []byte) (int, error) { return 0, errors.New("Test Error") } func TestAppendBytes(t *testing.T) { b := []byte(``) enc := NewEncoder(nil) enc.buf = b enc.AppendBytes([]byte(`true`)) assert.Equal(t, string(enc.buf), `true`, "string(enc.buf) should equal to true") } func TestAppendByte(t *testing.T) { b := []byte(``) enc := NewEncoder(nil) enc.buf = b enc.AppendByte(1) assert.Equal(t, enc.buf[0], uint8(0x1), "b[0] should equal to 1") } func TestAppendString(t *testing.T) { b := []byte(``) enc := NewEncoder(nil) enc.buf = b enc.AppendString("true") assert.Equal(t, string(enc.buf), `"true"`, "string(enc.buf) should equal to true") } func TestBuf(t *testing.T) { b := []byte(`test`) enc := NewEncoder(nil) enc.buf = b assert.Equal(t, b, enc.Buf(), "enc.Buf() should equal to b") } golang-github-francoispqt-gojay-1.2.13/encode_time.go000066400000000000000000000035651365331665100226070ustar00rootroot00000000000000package gojay import ( "time" ) // EncodeTime encodes a *time.Time to JSON with the given format func (enc *Encoder) EncodeTime(t *time.Time, format string) error { if enc.isPooled == 1 { panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder")) } _, _ = enc.encodeTime(t, format) _, err := enc.Write() if err != nil { return err } return nil } // encodeInt encodes an int to JSON func (enc *Encoder) encodeTime(t *time.Time, format string) ([]byte, error) { enc.writeByte('"') enc.buf = t.AppendFormat(enc.buf, format) enc.writeByte('"') return enc.buf, nil } // AddTimeKey adds an *time.Time to be encoded with the given format, must be used inside an object as it will encode a key func (enc *Encoder) AddTimeKey(key string, t *time.Time, format string) { enc.TimeKey(key, t, format) } // TimeKey adds an *time.Time to be encoded with the given format, must be used inside an object as it will encode a key func (enc *Encoder) TimeKey(key string, t *time.Time, format string) { if enc.hasKeys { if !enc.keyExists(key) { return } } enc.grow(10 + len(key)) r := enc.getPreviousRune() if r != '{' { enc.writeTwoBytes(',', '"') } else { enc.writeByte('"') } enc.writeStringEscape(key) enc.writeBytes(objKeyStr) enc.buf = t.AppendFormat(enc.buf, format) enc.writeByte('"') } // AddTime adds an *time.Time to be encoded with the given format, must be used inside a slice or array encoding (does not encode a key) func (enc *Encoder) AddTime(t *time.Time, format string) { enc.Time(t, format) } // Time adds an *time.Time to be encoded with the given format, must be used inside a slice or array encoding (does not encode a key) func (enc *Encoder) Time(t *time.Time, format string) { enc.grow(10) r := enc.getPreviousRune() if r != '[' { enc.writeByte(',') } enc.writeByte('"') enc.buf = t.AppendFormat(enc.buf, format) enc.writeByte('"') } golang-github-francoispqt-gojay-1.2.13/encode_time_test.go000066400000000000000000000070511365331665100236400ustar00rootroot00000000000000package gojay import ( "strings" "testing" "time" "github.com/stretchr/testify/assert" ) func TestEncodeTime(t *testing.T) { testCases := []struct { name string tt string format string expectedJSON string err bool }{ { name: "basic", tt: "2018-02-01", format: "2006-01-02", expectedJSON: `"2018-02-01"`, err: false, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { b := strings.Builder{} tt, err := time.Parse(testCase.format, testCase.tt) assert.Nil(t, err) enc := NewEncoder(&b) err = enc.EncodeTime(&tt, testCase.format) if !testCase.err { assert.Nil(t, err) assert.Equal(t, testCase.expectedJSON, b.String()) } }) } t.Run("encode-time-pool-error", func(t *testing.T) { builder := &strings.Builder{} enc := NewEncoder(builder) enc.isPooled = 1 defer func() { err := recover() assert.NotNil(t, err, "err should not be nil") assert.IsType(t, InvalidUsagePooledEncoderError(""), err, "err should be of type InvalidUsagePooledEncoderError") }() _ = enc.EncodeTime(&time.Time{}, "") assert.True(t, false, "should not be called as encoder should have panicked") }) t.Run("write-error", func(t *testing.T) { w := TestWriterError("") enc := BorrowEncoder(w) defer enc.Release() err := enc.EncodeTime(&time.Time{}, "") assert.NotNil(t, err, "err should not be nil") }) } func TestAddTimeKey(t *testing.T) { testCases := []struct { name string tt string format string expectedJSON string baseJSON string err bool }{ { name: "basic", tt: "2018-02-01", format: "2006-01-02", baseJSON: "{", expectedJSON: `{"test":"2018-02-01"`, err: false, }, { name: "basic", tt: "2018-02-01", format: "2006-01-02", baseJSON: `{""`, expectedJSON: `{"","test":"2018-02-01"`, err: false, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { b := strings.Builder{} tt, err := time.Parse(testCase.format, testCase.tt) assert.Nil(t, err) enc := NewEncoder(&b) enc.writeString(testCase.baseJSON) enc.AddTimeKey("test", &tt, testCase.format) enc.Write() if !testCase.err { assert.Nil(t, err) assert.Equal(t, testCase.expectedJSON, b.String()) } }) } } func TestAddTime(t *testing.T) { testCases := []struct { name string tt string format string expectedJSON string baseJSON string err bool }{ { name: "basic", tt: "2018-02-01", format: "2006-01-02", baseJSON: "[", expectedJSON: `["2018-02-01"`, err: false, }, { name: "basic", tt: "2018-02-01", format: "2006-01-02", baseJSON: "[", expectedJSON: `["2018-02-01"`, err: false, }, { name: "basic", tt: "2018-02-01", format: "2006-01-02", baseJSON: `[""`, expectedJSON: `["","2018-02-01"`, err: false, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { b := strings.Builder{} tt, err := time.Parse(testCase.format, testCase.tt) assert.Nil(t, err) enc := NewEncoder(&b) enc.writeString(testCase.baseJSON) enc.AddTime(&tt, testCase.format) enc.Write() if !testCase.err { assert.Nil(t, err) assert.Equal(t, testCase.expectedJSON, b.String()) } }) } } golang-github-francoispqt-gojay-1.2.13/errors.go000066400000000000000000000043771365331665100216520ustar00rootroot00000000000000package gojay import ( "errors" "fmt" ) const invalidJSONCharErrorMsg = "Invalid JSON, wrong char '%c' found at position %d" // InvalidJSONError is a type representing an error returned when // Decoding encounters invalid JSON. type InvalidJSONError string func (err InvalidJSONError) Error() string { return string(err) } func (dec *Decoder) raiseInvalidJSONErr(pos int) error { var c byte if len(dec.data) > pos { c = dec.data[pos] } dec.err = InvalidJSONError( fmt.Sprintf( invalidJSONCharErrorMsg, c, pos, ), ) return dec.err } const invalidUnmarshalErrorMsg = "Cannot unmarshal JSON to type '%T'" // InvalidUnmarshalError is a type representing an error returned when // Decoding cannot unmarshal JSON to the receiver type for various reasons. type InvalidUnmarshalError string func (err InvalidUnmarshalError) Error() string { return string(err) } func (dec *Decoder) makeInvalidUnmarshalErr(v interface{}) error { return InvalidUnmarshalError( fmt.Sprintf( invalidUnmarshalErrorMsg, v, ), ) } const invalidMarshalErrorMsg = "Invalid type %T provided to Marshal" // InvalidMarshalError is a type representing an error returned when // Encoding did not find the proper way to encode type InvalidMarshalError string func (err InvalidMarshalError) Error() string { return string(err) } // NoReaderError is a type representing an error returned when // decoding requires a reader and none was given type NoReaderError string func (err NoReaderError) Error() string { return string(err) } // InvalidUsagePooledDecoderError is a type representing an error returned // when decoding is called on a still pooled Decoder type InvalidUsagePooledDecoderError string func (err InvalidUsagePooledDecoderError) Error() string { return string(err) } // InvalidUsagePooledEncoderError is a type representing an error returned // when decoding is called on a still pooled Encoder type InvalidUsagePooledEncoderError string func (err InvalidUsagePooledEncoderError) Error() string { return string(err) } // ErrUnmarshalPtrExpected is the error returned when unmarshal expects a pointer value, // When using `dec.ObjectNull` or `dec.ArrayNull` for example. var ErrUnmarshalPtrExpected = errors.New("Cannot unmarshal to given value, a pointer is expected") golang-github-francoispqt-gojay-1.2.13/examples/000077500000000000000000000000001365331665100216125ustar00rootroot00000000000000golang-github-francoispqt-gojay-1.2.13/examples/encode-decode-map/000077500000000000000000000000001365331665100250435ustar00rootroot00000000000000golang-github-francoispqt-gojay-1.2.13/examples/encode-decode-map/main.go000066400000000000000000000035131365331665100263200ustar00rootroot00000000000000package main import ( "log" "strings" "github.com/francoispqt/gojay" ) // define our custom map type implementing MarshalerJSONObject and UnmarshalerJSONObject type myMap map[string]string // Implementing Unmarshaler func (m myMap) UnmarshalJSONObject(dec *gojay.Decoder, k string) error { str := "" err := dec.AddString(&str) if err != nil { return err } m[k] = str return nil } // Her we put the number of keys // If number of keys is unknown return 0, it will parse all keys func (m myMap) NKeys() int { return 0 } // Implementing Marshaler func (m myMap) MarshalJSONObject(enc *gojay.Encoder) { for k, v := range m { enc.AddStringKey(k, v) } } func (m myMap) IsNil() bool { return m == nil } // Using Marshal / Unmarshal API func marshalAPI(m myMap) error { b, err := gojay.Marshal(m) if err != nil { return err } log.Print(string(b)) nM := myMap(make(map[string]string)) err = gojay.Unmarshal(b, nM) if err != nil { return err } log.Print(nM) return nil } // Using Encode / Decode API func encodeAPI(m myMap) error { // we use strings.Builder as it implements io.Writer builder := &strings.Builder{} enc := gojay.BorrowEncoder(builder) defer enc.Release() // encode err := enc.EncodeObject(m) if err != nil { return err } log.Print(builder.String()) // make our new map which will receive the decoded JSON nM := myMap(make(map[string]string)) // get our decoder with an io.Reader dec := gojay.BorrowDecoder(strings.NewReader(builder.String())) defer dec.Release() // decode err = dec.DecodeObject(nM) if err != nil { return err } log.Print(nM) return nil } func main() { // make our map to be encoded m := myMap(map[string]string{ "test": "test", "test2": "test2", }) err := marshalAPI(m) if err != nil { log.Fatal(err) } err = encodeAPI(m) if err != nil { log.Fatal(err) } } golang-github-francoispqt-gojay-1.2.13/examples/fuzz/000077500000000000000000000000001365331665100226105ustar00rootroot00000000000000golang-github-francoispqt-gojay-1.2.13/examples/fuzz/Makefile000066400000000000000000000003611365331665100242500ustar00rootroot00000000000000.PHONY: gofuzzbuild gofuzzbuild: go-fuzz-build github.com/francoispqt/gojay/examples/fuzz .PHONY: gofuzz gofuzz: go-fuzz -bin=fuzz-fuzz.zip -workdir=. .PHONY: gofuzzclean gofuzzclean: rm -rf corpus crashers suppressions fuzz-fuzz.zipgolang-github-francoispqt-gojay-1.2.13/examples/fuzz/main.go000066400000000000000000000013721365331665100240660ustar00rootroot00000000000000package fuzz import ( "github.com/francoispqt/gojay" ) type user struct { id int created uint64 age float64 name string email string friend *user } // implement gojay.UnmarshalerJSONObject func (u *user) UnmarshalJSONObject(dec *gojay.Decoder, key string) error { switch key { case "id": return dec.Int(&u.id) case "created": return dec.Uint64(&u.created) case "age": return dec.Float(&u.age) case "name": return dec.String(&u.name) case "email": return dec.String(&u.email) case "friend": uu := &user{} return dec.Object(uu) } return nil } func (u *user) NKeys() int { return 3 } func Fuzz(input []byte) int { u := &user{} err := gojay.UnmarshalJSONObject(input, u) if err != nil { return 0 } return 1 } golang-github-francoispqt-gojay-1.2.13/examples/http-benchmarks/000077500000000000000000000000001365331665100247045ustar00rootroot00000000000000golang-github-francoispqt-gojay-1.2.13/examples/http-benchmarks/Makefile000066400000000000000000000001051365331665100263400ustar00rootroot00000000000000 .PHONY: bench bench: wrk -d 20s -s post.lua http://localhost:3000golang-github-francoispqt-gojay-1.2.13/examples/http-benchmarks/README.md000066400000000000000000000025201365331665100261620ustar00rootroot00000000000000# HTTP BENCHMARKS This package has two different implementation of a basic HTTP server in Go. It just takes a JSON body, unmarshals it and marshals it back to the response. It required `wrk` benchmarking tool, which you can find here: https://github.com/wg/wrk ## How to run ### gojay ```bash cd /path/to/package && go run gojay/main.go ``` Then run: ```bash cd /path/to/package && make bench ``` ### standard package (encoding/json) ```bash cd /path/to/package && go run standard/main.go ``` Then run: ```bash cd /path/to/package && make bench ``` ## Results Results presented here are ran on a MacBook Pro Mid 2015 2,2 GHz Intel Core i7 with 16G of RAM **gojay results:** ``` Running 20s test @ http://localhost:3000 2 threads and 10 connections Thread Stats Avg Stdev Max +/- Stdev Latency 298.77us 341.40us 10.52ms 94.13% Req/Sec 18.88k 1.89k 21.40k 73.63% 755246 requests in 20.10s, 1.67GB read Requests/sec: 37573.84 Transfer/sec: 84.85MB ``` **standard results:** ``` Running 20s test @ http://localhost:3000 2 threads and 10 connections Thread Stats Avg Stdev Max +/- Stdev Latency 613.21us 557.50us 12.65ms 93.88% Req/Sec 9.18k 423.20 10.10k 80.50% 365404 requests in 20.00s, 811.60MB read Requests/sec: 18269.66 Transfer/sec: 40.58MB ```golang-github-francoispqt-gojay-1.2.13/examples/http-benchmarks/gojay/000077500000000000000000000000001365331665100260155ustar00rootroot00000000000000golang-github-francoispqt-gojay-1.2.13/examples/http-benchmarks/gojay/main.go000066400000000000000000000056661365331665100273050ustar00rootroot00000000000000package main import ( "log" "net/http" "github.com/francoispqt/gojay" ) func main() { log.Println("Listening on port 3000") log.Fatal(http.ListenAndServe(":3000", http.HandlerFunc(handler))) } func handler(w http.ResponseWriter, r *http.Request) { var body Body dec := gojay.BorrowDecoder(r.Body) defer dec.Release() err := dec.Decode(&body) if err != nil { panic(err) } w.Header().Set("Content-Type", "application/json") enc := gojay.BorrowEncoder(w) defer enc.Release() err = enc.Encode(&body) if err != nil { panic(err) } } type Body struct { Colors *colors `json:"colors"` } func (c *Body) UnmarshalJSONObject(dec *gojay.Decoder, k string) error { switch k { case "colors": cols := make(colors, 0) c.Colors = &cols return dec.Array(c.Colors) } return nil } func (b *Body) NKeys() int { return 1 } func (b *Body) MarshalJSONObject(enc *gojay.Encoder) { enc.ArrayKey("colors", b.Colors) } func (b *Body) IsNil() bool { return b == nil } type colors []*Color func (b *colors) UnmarshalJSONArray(dec *gojay.Decoder) error { color := &Color{} if err := dec.Object(color); err != nil { return err } *b = append(*b, color) return nil } func (b *colors) MarshalJSONArray(enc *gojay.Encoder) { for _, color := range *b { enc.Object(color) } } func (b *colors) IsNil() bool { return len(*b) == 0 } type Color struct { Color string `json:"color,omitempty"` Category string `json:"category,omitempty"` Type string `json:"type,omitempty"` Code *Code `json:"code,omitempty"` } func (b *Color) UnmarshalJSONObject(dec *gojay.Decoder, k string) error { switch k { case "color": return dec.String(&b.Color) case "category": return dec.String(&b.Category) case "type": return dec.String(&b.Type) case "code": b.Code = &Code{} return dec.Object(b.Code) } return nil } func (b *Color) NKeys() int { return 4 } func (b *Color) MarshalJSONObject(enc *gojay.Encoder) { enc.ObjectKey("code", b.Code) enc.StringKey("color", b.Color) enc.StringKey("category", b.Category) enc.StringKey("type", b.Type) } func (b *Color) IsNil() bool { return b == nil } type Code struct { RGBA *ints `json:"rgba,omitempty"` Hex string `json:"hex,omitempty"` } func (c *Code) UnmarshalJSONObject(dec *gojay.Decoder, k string) error { switch k { case "rgba": rgba := make(ints, 0) c.RGBA = &rgba return dec.Array(&rgba) case "hex": return dec.String(&c.Hex) } return nil } func (b *Code) NKeys() int { return 2 } func (b *Code) MarshalJSONObject(enc *gojay.Encoder) { enc.ArrayKey("rgba", b.RGBA) enc.StringKey("hex", b.Hex) } func (b *Code) IsNil() bool { return b == nil } type ints []int func (v *ints) UnmarshalJSONArray(dec *gojay.Decoder) error { var i int if err := dec.Int(&i); err != nil { return err } *v = append(*v, i) return nil } func (v *ints) MarshalJSONArray(enc *gojay.Encoder) { for _, i := range *v { enc.Int(i) } } func (v *ints) IsNil() bool { return v == nil || len(*v) == 0 } golang-github-francoispqt-gojay-1.2.13/examples/http-benchmarks/post.lua000066400000000000000000000104031365331665100263720ustar00rootroot00000000000000wrk.method = "POST" wrk.body = [=[ { "colors": [ { "color": "black", "category": "hue", "type": "primary", "code": { "rgba": [255,255,255,1], "hex": "#000" } }, { "color": "white", "category": "value", "code": { "rgba": [0,0,0,1], "hex": "#FFF" } }, { "color": "red", "category": "hue", "type": "primary", "code": { "rgba": [255,0,0,1], "hex": "#FF0" } }, { "color": "blue", "category": "hue", "type": "primary", "code": { "rgba": [0,0,255,1], "hex": "#00F" } }, { "color": "yellow", "category": "hue", "type": "primary", "code": { "rgba": [255,255,0,1], "hex": "#FF0" } }, { "color": "green", "category": "hue", "type": "secondary", "code": { "rgba": [0,255,0,1], "hex": "#0F0" } }, { "color": "black", "category": "hue", "type": "primary", "code": { "rgba": [255,255,255,1], "hex": "#000" } }, { "color": "white", "category": "value", "code": { "rgba": [0,0,0,1], "hex": "#FFF" } }, { "color": "red", "category": "hue", "type": "primary", "code": { "rgba": [255,0,0,1], "hex": "#FF0" } }, { "color": "blue", "category": "hue", "type": "primary", "code": { "rgba": [0,0,255,1], "hex": "#00F" } }, { "color": "yellow", "category": "hue", "type": "primary", "code": { "rgba": [255,255,0,1], "hex": "#FF0" } }, { "color": "green", "category": "hue", "type": "secondary", "code": { "rgba": [0,255,0,1], "hex": "#0F0" } }, { "color": "black", "category": "hue", "type": "primary", "code": { "rgba": [255,255,255,1], "hex": "#000" } }, { "color": "white", "category": "value", "code": { "rgba": [0,0,0,1], "hex": "#FFF" } }, { "color": "red", "category": "hue", "type": "primary", "code": { "rgba": [255,0,0,1], "hex": "#FF0" } }, { "color": "blue", "category": "hue", "type": "primary", "code": { "rgba": [0,0,255,1], "hex": "#00F" } }, { "color": "yellow", "category": "hue", "type": "primary", "code": { "rgba": [255,255,0,1], "hex": "#FF0" } }, { "color": "green", "category": "hue", "type": "secondary", "code": { "rgba": [0,255,0,1], "hex": "#0F0" } }, { "color": "black", "category": "hue", "type": "primary", "code": { "rgba": [255,255,255,1], "hex": "#000" } }, { "color": "white", "category": "value", "code": { "rgba": [0,0,0,1], "hex": "#FFF" } }, { "color": "red", "category": "hue", "type": "primary", "code": { "rgba": [255,0,0,1], "hex": "#FF0" } }, { "color": "blue", "category": "hue", "type": "primary", "code": { "rgba": [0,0,255,1], "hex": "#00F" } }, { "color": "yellow", "category": "hue", "type": "primary", "code": { "rgba": [255,255,0,1], "hex": "#FF0" } }, { "color": "green", "category": "hue", "type": "secondary", "code": { "rgba": [0,255,0,1], "hex": "#0F0" } } ] } ]=] wrk.headers["Content-Type"] = "application/json" golang-github-francoispqt-gojay-1.2.13/examples/http-benchmarks/standard/000077500000000000000000000000001365331665100265045ustar00rootroot00000000000000golang-github-francoispqt-gojay-1.2.13/examples/http-benchmarks/standard/main.go000066400000000000000000000014431365331665100277610ustar00rootroot00000000000000package main import ( "encoding/json" "log" "net/http" ) func main() { log.Println("Listening on port 3000") log.Fatal(http.ListenAndServe(":3000", http.HandlerFunc(handler))) } func handler(w http.ResponseWriter, r *http.Request) { var body Body err := json.NewDecoder(r.Body).Decode(&body) if err != nil { panic(err) } w.Header().Set("Content-Type", "application/json") err = json.NewEncoder(w).Encode(body) if err != nil { panic(err) } } type Body struct { Colors []Color `json:"colors"` } type Color struct { Color string `json:"color,omitempty"` Category string `json:"category,omitempty"` Type string `json:"type,omitempty"` Code Code `json:"code,omitempty"` } type Code struct { RGBA []int `json:"rgba,omitempty"` Hex string `json:"hex,omitempty"` } golang-github-francoispqt-gojay-1.2.13/examples/http-json/000077500000000000000000000000001365331665100235405ustar00rootroot00000000000000golang-github-francoispqt-gojay-1.2.13/examples/http-json/main.go000066400000000000000000000023261365331665100250160ustar00rootroot00000000000000package main import ( "net/http" "github.com/francoispqt/gojay" ) type message struct { foo string bar string } func (m *message) UnmarshalJSONObject(dec *gojay.Decoder, k string) error { switch k { case "foo": return dec.AddString(&m.foo) case "bar": return dec.AddString(&m.bar) } return nil } func (m *message) NKeys() int { return 2 } func (m *message) MarshalJSONObject(dec *gojay.Encoder) { dec.AddStringKey("foo", m.foo) dec.AddStringKey("bar", m.bar) } func (m *message) IsNil() bool { return m == nil } func home(w http.ResponseWriter, r *http.Request) { // read body using io.Reader m := &message{} dec := gojay.BorrowDecoder(r.Body) defer dec.Release() err := dec.DecodeObject(m) if err != nil { i, err := w.Write([]byte(err.Error())) if err != nil || i == 0 { panic(err) } return } // just transform response slightly m.foo += "hey" // return response using io.Writer enc := gojay.BorrowEncoder(w) defer enc.Release() err = enc.Encode(m) if err != nil { i, err := w.Write([]byte(err.Error())) if err != nil || i == 0 { panic(err) } } return } func main() { http.HandleFunc("/", home) if err := http.ListenAndServe(":8080", nil); err != nil { panic(err) } } golang-github-francoispqt-gojay-1.2.13/examples/websocket/000077500000000000000000000000001365331665100236005ustar00rootroot00000000000000golang-github-francoispqt-gojay-1.2.13/examples/websocket/client/000077500000000000000000000000001365331665100250565ustar00rootroot00000000000000golang-github-francoispqt-gojay-1.2.13/examples/websocket/client/client.go000066400000000000000000000006341365331665100266660ustar00rootroot00000000000000package client import ( "github.com/francoispqt/gojay/examples/websocket/comm" "golang.org/x/net/websocket" ) type client struct { comm.SenderReceiver id int } func NewClient(id int) *client { c := new(client) c.id = id return c } func (c *client) Dial(url, origin string) error { conn, err := websocket.Dial(url, "", origin) if err != nil { return err } c.Conn = conn c.Init(10) return nil } golang-github-francoispqt-gojay-1.2.13/examples/websocket/comm/000077500000000000000000000000001365331665100245335ustar00rootroot00000000000000golang-github-francoispqt-gojay-1.2.13/examples/websocket/comm/comm.go000066400000000000000000000041431365331665100260170ustar00rootroot00000000000000package comm import ( "errors" "log" "github.com/francoispqt/gojay" "golang.org/x/net/websocket" ) // A basic message for our WebSocket app type Message struct { Message string UserName string } func (m *Message) UnmarshalJSONObject(dec *gojay.Decoder, k string) error { switch k { case "message": return dec.AddString(&m.Message) case "userName": return dec.AddString(&m.UserName) } return nil } func (m *Message) NKeys() int { return 2 } func (m *Message) MarshalJSONObject(enc *gojay.Encoder) { enc.AddStringKey("message", m.Message) enc.AddStringKey("userName", m.UserName) } func (u *Message) IsNil() bool { return u == nil } // Here are defined our communication types type Sender chan gojay.MarshalerJSONObject func (s Sender) MarshalStream(enc *gojay.StreamEncoder) { select { case <-enc.Done(): return case m := <-s: enc.AddObject(m) } } type Receiver chan *Message func (s Receiver) UnmarshalStream(dec *gojay.StreamDecoder) error { m := &Message{} if err := dec.AddObject(m); err != nil { return err } s <- m return nil } type SenderReceiver struct { Send Sender Receive Receiver Dec *gojay.StreamDecoder Enc *gojay.StreamEncoder Conn *websocket.Conn } func (sc *SenderReceiver) SetReceiver() { sc.Receive = Receiver(make(chan *Message)) sc.Dec = gojay.Stream.BorrowDecoder(sc.Conn) go sc.Dec.DecodeStream(sc.Receive) } func (sc *SenderReceiver) SetSender(nCons int) { sc.Send = Sender(make(chan gojay.MarshalerJSONObject)) sc.Enc = gojay.Stream.BorrowEncoder(sc.Conn).NConsumer(nCons).LineDelimited() go sc.Enc.EncodeStream(sc.Send) } func (sc *SenderReceiver) SendMessage(m gojay.MarshalerJSONObject) error { select { case <-sc.Enc.Done(): return errors.New("sender closed") case sc.Send <- m: log.Print("message sent by client: ", m) return nil } } func (c *SenderReceiver) OnMessage(f func(*Message)) error { for { select { case <-c.Dec.Done(): return errors.New("receiver closed") case m := <-c.Receive: f(m) } } } func (sc *SenderReceiver) Init(sender int) *SenderReceiver { sc.SetSender(sender) sc.SetReceiver() return sc } golang-github-francoispqt-gojay-1.2.13/examples/websocket/main.go000066400000000000000000000043051365331665100250550ustar00rootroot00000000000000// package main simulates a conversation between // a given set of websocket clients and a server. // // It spins up a web socket server. // On a client's connection it creates a SenderReceiver which handles JSON Stream // encoding and decoding using gojay's streaming API to abstract JSON communication // between server and client, only having to handle go values. // // To simulate a conversation: // - the server sends a welcome message to the client // - when the client receives the message, it sends a message back to the server // - when the server receives the ack message, it will send a message randomly to a client // - when the client receives the message, it sends a message back to the server... and so on. package main import ( "log" "strconv" "github.com/francoispqt/gojay/examples/websocket/client" "github.com/francoispqt/gojay/examples/websocket/comm" "github.com/francoispqt/gojay/examples/websocket/server" ) func createServer(done chan error) { // create our server, with a done signal s := server.NewServer() // set our connection handler s.OnConnection(func(c *server.Client) { // send welcome message to initiate the conversation c.SendMessage(&comm.Message{ UserName: "server", Message: "Welcome !", }) // start handling messages c.OnMessage(func(m *comm.Message) { log.Print("message received from client: ", m) s.BroadCastRandom(c, m) }) }) go s.Listen(":8070", done) } func createClient(url, origin string, i int) { // create our client c := client.NewClient(i) // Dial connection to the WS server err := c.Dial(url, origin) if err != nil { panic(err) } str := strconv.Itoa(i) // Init client's sender and receiver // Set the OnMessage handler c.OnMessage(func(m *comm.Message) { log.Print("client "+str+" received from "+m.UserName+" message: ", m) c.SendMessage(&comm.Message{ UserName: str, Message: "Responding to: " + m.UserName + " | old message: " + m.Message, }) }) } // Our main function func main() { done := make(chan error) createServer(done) // add our clients connection for i := 0; i < 100; i++ { i := i go createClient("ws://localhost:8070/ws", "http://localhost/", i) } // handle server's termination log.Fatal(<-done) } golang-github-francoispqt-gojay-1.2.13/examples/websocket/server/000077500000000000000000000000001365331665100251065ustar00rootroot00000000000000golang-github-francoispqt-gojay-1.2.13/examples/websocket/server/server.go000066400000000000000000000027221365331665100267460ustar00rootroot00000000000000package server import ( "log" "math/rand" "net/http" "sync" "time" "github.com/francoispqt/gojay/examples/websocket/comm" "golang.org/x/net/websocket" ) type server struct { clients []*Client mux *sync.RWMutex handle func(c *Client) } type Client struct { comm.SenderReceiver server *server } func NewClient(s *server, conn *websocket.Conn) *Client { sC := new(Client) sC.Conn = conn sC.server = s return sC } func NewServer() *server { s := new(server) s.mux = new(sync.RWMutex) s.clients = make([]*Client, 0, 100) return s } func (c *Client) Close() { c.Conn.Close() } func (s *server) Handle(conn *websocket.Conn) { defer func() { err := conn.Close() if err != nil { log.Fatal(err) } }() c := NewClient(s, conn) // add our server client to the list of clients s.mux.Lock() s.clients = append(s.clients, c) s.mux.Unlock() // init Client's sender and receiver c.Init(10) s.handle(c) // block until reader is done <-c.Dec.Done() } func (s *server) Listen(port string, done chan error) { http.Handle("/ws", websocket.Handler(s.Handle)) done <- http.ListenAndServe(port, nil) } func (s *server) OnConnection(h func(c *Client)) { s.handle = h } func random(min, max int) int { rand.Seed(time.Now().Unix()) return rand.Intn(max-min) + min } func (s *server) BroadCastRandom(sC *Client, m *comm.Message) { m.Message = "Random message" s.mux.RLock() r := random(0, len(s.clients)) s.clients[r].SendMessage(m) s.mux.RUnlock() } golang-github-francoispqt-gojay-1.2.13/go.mod000066400000000000000000000017021365331665100211020ustar00rootroot00000000000000module github.com/francoispqt/gojay go 1.12 require ( cloud.google.com/go v0.37.0 // indirect github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23 github.com/go-errors/errors v1.0.1 github.com/golang/protobuf v1.3.1 // indirect github.com/json-iterator/go v1.1.6 github.com/lunixbochs/vtclean v1.0.0 // indirect github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.1 // indirect github.com/pkg/errors v0.8.1 // indirect github.com/stretchr/testify v1.2.2 github.com/viant/assertly v0.4.8 github.com/viant/toolbox v0.24.0 golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a // indirect golang.org/x/net v0.0.0-20190313220215-9f648a60d977 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421 // indirect golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f // indirect gopkg.in/yaml.v2 v2.2.2 // indirect ) golang-github-francoispqt-gojay-1.2.13/go.sum000066400000000000000000000445711365331665100211420ustar00rootroot00000000000000cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.37.0 h1:69FNAINiZfsEuwH3fKq8QrAAnHz+2m4XL4kVYi5BX0Q= cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo= dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU= dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU= dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4= dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU= git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23 h1:D21IyuvjDCshj1/qq+pCNd3VZOAEI9jy6Bi131YlXgI= github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= github.com/json-iterator/go v1.1.6 h1:MrUvLMLTMxbqFJ9kzlvat/rYZqZnW3u4wkLzWTaFwKs= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/lunixbochs/vtclean v1.0.0 h1:xu2sLAri4lGiovBDQKxl5mrXyESr3gUr5m5SM5+LVb8= github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe h1:W/GaMY0y69G4cFlmsC6B9sbuo2fP8OFP1ABjt4kPz+w= github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY= github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM= github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0= github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw= github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI= github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU= github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag= github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg= github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw= github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y= github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q= github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ= github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I= github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0= github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ= github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk= github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4= github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw= github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= github.com/viant/assertly v0.4.8 h1:5x1GzBaRteIwTr5RAGFVG14uNeRFxVNbXPWrK2qAgpc= github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU= github.com/viant/toolbox v0.24.0 h1:6TteTDQ68CjgcCe8wH3D3ZhUQQOJXMTbj/D9rkk2a1k= github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw= golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a h1:YX8ljsm6wXlHZO+aRz9Exqr0evNhKRNe5K/gi+zKh4U= golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190313220215-9f648a60d977 h1:actzWV6iWn3GLqN8dZjzsB+CLt+gaV2+wsxroxiQI8I= golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421 h1:Wo7BWFiOk0QRFMLYMqJGFMd9CgUAcGx7V+qEg/h5IBI= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f h1:yCrMx/EeIue0+Qca57bWZS7VX6ymEoypmhWyPhz0NHM= golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck= sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0= golang-github-francoispqt-gojay-1.2.13/gojay.go000066400000000000000000000010461365331665100214350ustar00rootroot00000000000000// Package gojay implements encoding and decoding of JSON as defined in RFC 7159. // The mapping between JSON and Go values is described // in the documentation for the Marshal and Unmarshal functions. // // It aims at performance and usability by relying on simple interfaces // to decode and encode structures, slices, arrays and even channels. // // On top of the simple interfaces to implement, gojay provides lots of helpers to decode and encode // multiple of different types natively such as bit.Int, sql.NullString or time.Time package gojay golang-github-francoispqt-gojay-1.2.13/gojay.png000066400000000000000000001262031365331665100216170ustar00rootroot00000000000000‰PNG  IHDRô&¥Èx3 pHYs × ×B(›xtIMEâ-4‡wsž IDATxÚìwxÕúÇ¿ïÙdg6¤PÔkv…)6»‚—k»z­\ ^I°bаA@ìzïµ]»¨T) v þ”’²3›ìyì¬c€ÔÝMò~ž‡‡™ÙÝ™“Ó¾ç=sÎû‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚$YÐ2ËÕçóÂÌÞ`0˜ ®Úç•§,|¢”ò———[{¹ßÉRAt¡‰ñz½] ¢îŽp_£U¢{,˺[rZA]h'¢áþ àð&~\±mÛ%×ADÐ…Æòc‰h €þÕ?cæmDô9€/™ù¥Ô/ZëÿSJíªþ]­u[¤”Jàaæ6Ìì­f™ß k­Ó+**ÖI ‚ B1M3Ã0 Ë0 ÿóz½+ ÃåóùÀùÏ0Œ%®g}…FœÆA„V‰a úöU¯×{lc‹x5AË=x0 cŠ”„ ‚ ÔŸdÃ0¶º„5·)…¼º {½Þç¹AÓ4ûJq‚ ÄJ² YXç×ø‹sºÈ¶í±NÂ&»(f~2õ.‚ ‚.Ô‹¿†´Öã£ð| @xq”a÷J‘‚ ˆ  uÃàçø‹ŠŠŠ•ÑHDNiò£:§£âãã»KÑ‚ ˆ  µÄëõ Ù9}?šiqö½ïàñx<;ƒ AA]¨…ˆè:]Í´L.I*p/0sÃ0n”AAj3·wnŽvzrJ“s˜y…sú Ïç;TJIA]ØW)•>ÖZo‹‘4]ÐÔ{²ÖzÄã  ‚º°w´Öåá㸸¸˜xgíL½ß᜞eÆ))Ata/Ñn×iÌ,BË)M~Àbçt’aGIi 1D’išGÄÇÇw?)>>¾[RRÒ~’-BK&N² æ}3‚Áàþ1–¶ÁÌü€Df^àtA)5!’$$$¬µ>Ÿ™O&¢îÌÜ€—™¡T•Í`ÆnEÌ\@Dk˜ù£@ ð…ä¢ ºÐô¤ÔF—€vŠ¥´9Sï÷8i;Ù0ŒQRbBDÌ襤ý¼^ïí†a¬ ƒ?2ó<Ù¹ï^~Ú@W" ˆ>7 ã+¯×;1!!á ÉY¡Y€’±_F†aì ϶í¬H=Ø0Œ·\ÈÌ+sËRzïé{ÙÉ¥Ë뼂ˆÎ¶,k©›Ð$&&PQQq7€¢š bg6k} `€ŸÃŸ1sµp3ŸàTm«ÝÞð €I¶mo”Üš2åû0}ÍÌ'8)Fg®ÒZ¯°?3¿˜pRyyùÏRtB#lo¨¬¬|@;×õ_<¯”zaÛÊÇW¸Äù·O:ô¾ádf¾˜™¯p(@€kMÓœjYÖƒÅ0±Ð…F³”gÈ`Û¶mÄš…ãSÊú1óBgø¥mÛ}ìÒdÃ0ž0Ðum3MÜþé ãízë`€s\ýâ&­õ?***>•"DÐ…FÁëõ^CDÏ£µÞBD¤”jÇ̉Ììaæd·U¯”Ú Á`p‡ÇãÙaYÖ]5• À¸¤’Û‰èçô}Û¶/ê‹Ïç;\ký.€cœK3ß³sõ܇šây)'eôòx<³˜¹—s)`¼mÛ3 +,"èBHôù|]µÖ]tq:³þT^ÿÇÌÅJ©bÅÖk­×Ï*莨Ï'¢ÁÀÌÏë¥#êCBBÂÁÁ`p±Óàsf¾bçê¹_7õ³Û÷žÁÌSá¼gwêòP„Þ³ ‚ºP³!ìóùºk­O&¢“™¹;€TD~BÀÖÑÇZëD4µ®‚ÙÉ¥8Ûé' ·*Ô•6†a¬p¼sþþŽUsÎdÚ÷ž àEfîé\Zêõz/)++Û&Å#ˆ   âããÓ‰è":@/„ãÔ ff¥ÔfÞî¬ê- ‰¨Äõ Å´GhQ;TEm«‹È{Ñ%“K’6Õö‡ãSÊþÍÌÔZ÷®¨¨X#E/Ôz”ksZ  w¬šsN´ÒÒ®ç°ÙnvN¿Ž‹‹;s÷îÝ¿H) "è­“8Ã0Îfæ+ˆè"ìãû•6Ñç¾PDDEJ©¢iÓ'7hõøÈ¬±©ÌÜ@gçßñNpX-~¾žˆ^ƒ/MÙÕ¶ –úWmš2¤µó¿xÝ9]·cÕœnÑNSû^Ãïaæ‰Îé—ñññgîÚµëW)-A½•àóùNÖZfæËˆ¨Ã^¾º‰™—Ñr­õÊY³§¯FzGfí«µ>•ˆN#¢S«Ez«IÜ_ ¢y“v&~·A/GhÿüRÛ¶Ï!Ô‚xÃ0¾p›™{î\=÷³XHX»žÃƘâô›Ÿ9;9J¥Èô–K²a×`O–ÅÿÑBïчgäþ‹HæˆÑ]‰è\çèãˆsu4€w<žSšüZøâ¸¤’«ˆèy ¢,˺Jª†P ëüF3ß½sõÜ»c)}m{ ½‹ˆÂkBÞ´m{ ÓA½¥`šfGf‰Ð{¿¤¾²ÀKJ©çž‘»¢9þ™#F_¢”ºš™/B5/]›ÌÈ)Mžž\ú€9‚>̲¬¹RK„}õG†a|…ЮŽ-;VÍ9,Ù®ç°F:§Û¶-.ô–@|||šÇã¹™/ÅŸ£¡ÙDô*3ÏË›9õÝ–ôwÌûOfà´>.!¢Jfî Ô¶íCŠŸ.{ÄçóõÖZ‡»ãv¬š“«im×s؇ÎrNÏ·mû=)AA½™âõzUJÝÃÌ—×ßxb\ö¨äÃ;ôΖœ™#FODÃò­Ý¶†¯Ú¶}Äkœ° ÃÈ0€õÃâ™/¶I0ÅjZ÷;ùÆC´Ö_0s;ßÛ¶}ÄM¬ ‚Þ¼HHH8¸²²ò~"º¾‹| 3O›9kÚ«­1o²2ÇŒDh*2µÚG;™yR xâ˜Cس "´î䫿\ÔˆÖô1Ì|©RêD­u€ˆÖ*¥þ»måã Z»Ò¾×ðaÌ<ˆè>Ç£ ˆ 7L¯×;šˆnèºÎ^×Zß?köôU’M@æˆÑW8.`¬öQ13 /K. ÕhkÆv§O¹cÕœ ½áw?þ–‘~Émg3óU5ôuA³w¬šÓ è…m{ ý˜ˆNPîõz§3‚zì[ýÌЩ!¿'ZÛÌš°_NDwèZí£ÜdÛöÉ%|>ß©Zëå µîQ²&uCîס÷ ‡j­?DhûÛÞøhǪ9g6Àúïà-`æÛÀƒRš‚zlv2‡k­g¨>ý·Dk=jÖìéâý¬deŽ @G×åJض}äÝc«Ç4Í fž €w¬šÓà~©]Ïaï!´Ý²6LÛ±jÎèú>«}¯ákœðÆ?Ù¶Ý HA-¼^ï5J©™Î¢—0?Ñ­3ò¦<)9TwFfÄÌ£r'æK­õu’C­º½M ¢‡üߎUshȽÚ÷~&3T‡ŸT*¥:Ö÷zû^ÃÿÉÌá>¡¿mÛoK‰ Ñ@Iü‘¤¤¤†a¼LDϸÄ\Ñä¼™S1¯?3ò¦L ¢c¸;Ûã•RŸš¦y7þ¼ÈPh-–Q¸­5Øó3_\ÇŸÄ1sÿú>oË¢¼3z'Hi "è1€ÏçëV¸Äuù{fäM/9Ô(¢^”7s꙲ø]êDÃ0ÞLLLÜ_r©UPTÞ÷:¶ƒ€cêû0g{ÝgÎ}zJQ Ñ"N² „×믵¾¿ZžäåÍœš@¦Ð™¼™Ssœ@1O8ݹ|~EEÅ—¦i^iYÖG˜˜¸ ª”:‘™u:kwtº€;üÈÌ›lfæ×*** %§›…^ÎÌ@ÍëJ}fzdÜ0ó:"J'¢#¥4ôè‘`šæ\gkK˜Ÿ É›9U„¼i­õbÈ1zÝ ÀþÌüž×ë½#L¹D4Èéìk‹Øßùw$QhYˆR*³¢¢âˆŸífÖºÜ)»äF¸Ýg×ñ7ò@¥ÔÏNmÐÚ$–RDÐ#HRRÒ~¶m¿ÆÌ§º,…Õ.Ÿ‘7e³TÈ0sÖ´IY™cÎð,€ýÄÑ$Ã0bfwG»Àk̼ÝuÍ$¢vÌÜžˆÐ€ÉÌ ÃømÛ×¢q¦q…&D)õ£#ˆ ~åBDÿaæ›êð“ Rê5„¶¦þ½o8”™ fVZë”ê¿óx<¥Ž…>"l¬‹˜ Ñ¢Õ®rOSJ½àWG0YÞ•GGf?qô7߬¤šuõ!€£r*¶m;uo÷ðù|ÿÕZ_溴Զíþ_ò1išg2ó‡Ž8vmhÈÔv=‡-E(Bà>aæ•Dô)€TfÞˆp‰õxtÐxþFD?( ÿ#¢"¿ßÿ“¾ ‚Þˆø|¾S‚Áàÿ\1Ê+‰è¦ySæH•ˆ>™#F?IDÿt ´Š˜¹³Óù^þ³§ß†ñ?ð«ËÚQý6y¨Öz‹SÞƒ¶úÄ‚†Ü¯}¯á©Ì¼ ÀÁ1ö§–(P@DkµÖ@àT­’ôÚbÆ@/¡jaÕN"ºtFÞ”¥:ÄY™cn0 ^¬ô¦mÛöR¾‹ô°ˆˆ3óİ¥ïüΪÍóããã»Ñ#‚D´™·Wÿ_)õZëŸÀ÷ç8Ñ6¿p83Ïß¹zîàZè]ˆè:­õ×À}_B[ìüÛJDÛœ²ÞÆÌÛ”RÛ++++Ý?ˆ‹‹ój­Û2s["Jaæ¶J©¶Zë¿Ñ¡Í* aÏ.ð)}ÂÌ+lÛ^`»ÔA}ïFÿAÕJÚŸµÖÄukl22kìf~^ë±Lk=²¢¢bM e\ Àë9¥É³“Kǘâ|üšmÛ—#ä¥n_ue6œxÀ:"ZÎÌËmÛ^‰0W'LÓ|™¯°yǪ9ë!â¸ÀùºìákA?1ó"o3sÑ?Þ^zBÒ!‡å7ÕßUQ¾{Á“'?ÃãñœÀÌ' ä ùx{Š÷®‰h €w‰è¿ß¿²6uVZ †q€ÿ¢ÊKÙgçÍœ*þÄc˜Ì£/#¢çñGïráŽyŽmÛÀåRÖ0Œº0ó3¹e)×Àø”²{™ù.`ægÀ ìcõ»+”'Ñ*f6z§šèÌî¤ì#é6€ÿ)¥¦ùýþO¤$k%èC˜9¨½?÷v=‡]ÈÌÿ ¢‹÷P&%X¢”Z9xíÆ±ò÷ÅOö:fâ,Ìí ©†¯î$¢÷µÖ¯·d (´jA7 c€WÚ_ÆÅÅ3mú䟥 4 K½3¿çuvÕÝÜ`ÛöNYÿ€<šSšüûJçìäÒµº¡Å–eMØÛ3½^ï­Dô ä”îy'U¥Í‹oݯô&„‚€KDg!´¯Þ½Ÿúõ¸¸¸á»wïþEJs¯´7 c+€x"š¼ýÓ'j\ Ú¡÷ }´Ö׸¡]n@!½à!E‹›SÌéÚ1M)u–3Ëpþ¼/¿Àb¯y<ž—ËËË’j#´Aw¢8½ s©H)Õïá¹[¤ø›ζ¶×˜Î¥Ý®23/ƒ Ãð;`NNirvøóñ)eƒ™y¾sú…mÛ'îcx€ÇÀãñüÐŽ6uê8ǧ”eø—´~aæóÀgRš{Í÷7ôðÛŽUs~ëýO¹éÀÊÊÊë pL ³5ËüW)õòàµh)ù‘Ÿ–zBîdûãÏ‘ã‚> ¢g,Ëz²ŽC½%ÿq^¯÷x"Z ­sé{":Cö˜7[Qÿ+\¯Mˆh€¶.Ÿû›à„¸eæ;rËRîÿvlâÎÞJ©•aCȶíáû¨;#¢W@kÝ}Ê®¶õZg1>¥,ƒ™§:éÜæñxºûýþï¥4÷(è¼áœÞˆÐ«±a j† ½2Yàßqqq¯ Z½akKÏ›¹Ý:àR"º@Zµw1ó+Dô´mÛBVÍ‹ ·$LÓ<‚™?psi³RꌇgäJgÚ¼EýÇR7Q_àgfþ{µ¯ŽÌ)MžáÖs˜yaXßmÛžº·çÄÇÇ÷r úç”&¿Uß4O)ëÈÌ#´…j‘mÛgAö"ï eÆ·í /#¢êï”×3óSçéÁk7¶Ú¶™#¢á¨ ö"*qÿNkìúl_ Ú·Õõý’æÉ%I›µÖ;Ve?g·…ðgëühÃ0òèä{XÌË<¦”:%£°øè¡ë6=Кņeçfwp ݃C8DZDô™akMÓŠ}oÄBI<†a¼ à\çÜ&¢sgäMY"ÅÝrÈ1ún"ºÛué­õ"Ç3‡™Û)¥Îž´3ñ»ð‡cw¦+¥Â1׋œwèþ½ à Æä”&Omhš³“K_ð7 mÛ>WJ1„Ïç;]k à¢j}Ò—É(,ž-¹T;ætíØS)5ÀÕp­˜'¢Ì<À£¶mIN‰ 7 ¼^ïx"šäªÈ3ò¦äKQ·!®mãââ®yp{B£¸ ÎN.`:BûÑÏl}ŠiÆPãú{GC´š™ïÎ(,~CºÝØ#?-õY»úRÑ*­õä@ ð dzS§ß0Œ„;Ƶy3§vof">€ˆ®eæ‹ñg¿Í5Q à7gJ€R"²™Ù ™ˆÚ:‹¸Rü¥÷Ó…}zFÞ”ù͹2deŽ9 ¡XÓmkðÖܲ”‡b)ãSÊ®gæ§|eÛöñ-¨/1 Ã`þƒ¼ÀÝ…ůIwû,èqÔ•••£zåž Ü@D÷[–õÄ zõ 7xÄ9µ˜9½9L'geŽé À?ö"º€åD´¡éòoƒÁào@`}]žuÐ…ˆÒ‰è,fn¿‡¯û‰è5ÎÈ›²¸9Ö‰¬Ì17ï[0sznYÊ—±’¾ìäÒ‹¼ÁÌÛÀ~-Á"÷z½#«O­ѧnRPô¾t³ÍÖj¿@vµÚwDô eYó !]EЋÄÄÄý+++ׇýxÑ3ò¦ÜËiÎ1úX·Õð¾*ÌgDô6€…–e5YGß“ˆÎ!¢óŠèä©ákKˆè¾ySš]‡œ9bôJ"êíœ~Sš|v¬¤mBÛ]§j­?Û¶ÛØÙL› òz½W8ÊÜ+ö¿dæ{†®Ûô’t¯-ƒ¹Ý:M ¢QÕŒÏÜaÛ¶¬…Aoëü„¦…`}ÞÌ©GǰÀ¯”šÈÌ—ãÏ[·x^k½ ¢¢¢0Òióù|‡ƒÁkˆh€ãªî,bº;oæÔw›‘ w%¢Õp·1óŹe)¯Æˆ…~€  ”:µ®&V¦i^ËÌ·ÂõŒˆÖ˜ yËe^zçAÌ|»{ç´³ñ–e}(9$‚^_ ó$¥Ô§aq$¢³fäM‰É ••9f*B‹Mâ«}ô€i¶mÿ/†òµ'e:3ÕÓûš‘7¥¸9Ô‘‘Yc'3s8žö¦œÒäN±®J›ß¶Y_çtœmÛSšK»3Mó,­õýÕ¶:ýHDw )(N­G؇3ó]p-zð €[mÛ^/9$‚^Wëü]ç9§oæÍœ: …ü"º«ÚûêJf~@n,ÇÅöz½G+¥®f摨 = „¬<Ú\ödeŽùUïuÇæ”&Ç„xf'—îtòõ=Û¶ÏoèîJ©„v`„)a朡ë6= Ýhëdn·Nã•Rã]}\À Û¶ïC( Ž ‚¾O1?@xú×&¢ãfäM‰™`[¶üp_nÎô^ªwÔï1ó¨@ ðesÉkÇsn\\ÜŽÚp¿*Ø µ¾rÖìé1n¥cæ9azðפÂ8ƒúÆ€ /p€Ê¸¸¸ÃvïÞ“±¼¨¨¨xÀWù€g\V!| ³9/ñz½Ý‰Ç.z –â'Dt²s:-oæÔѱ®Ì£Ãþ¹Ã"²ˆ·Ä-^¯÷8"zÀ‰®Ëg@¬zœËÊóy8½é—½!ŒK*¹Ýð Û¶O°;ŠIjgF.BÓëá~a 3‘-hB]™Û­Ó5D4 U[Ý6¸Á¶mÙ!:`šf—˜û9æèÇcD,»ÅÀÛÁ`ð-uf øÊ¶íýÇaWéÁ`p͈›G¥Å¨•~ëøÖ±‰;G;M¹e)0s8vû †a<ÀŒÆ Þ4Íá¦iÈpÄ|€[2 ‹1êÃÐu›žÍ(,þ MFȳ\'ïy½Þ§P·xBK´Ð ÃxUÑÔ¢þîü‘ÙO½~ý†g]¾Ã`ªmÛcZKåñz½WÑ“ çÒ¯Zë‹fÍž¾2ÖÒš•9ækÇ:ÙÒ\r IDAT§A"zprIÒÑL“³…­-€°ÏùE¶m_‚9›iÓ¦Í_*++ð7×åÜ”QX,Û„Fa~÷.§i­p‚sé­õõ«$wZ¡…nÆÑ.¬2²xV4Ó3zÔøƒ¾ùfý.1gÙ­IÌkýy"€ª-*û+¥>™5öŒLîìßG±D¿0ó ÙÉ¥ïD3Aqõuȼé\êgƯ×{\ÚÔ_+++?s‰y)ÝQX|–ˆ¹Ð˜ ^»qyFañ N,öJÇ(¥–{½ÞÛÐÌÖq‰ 77¸ÒúV´ýµWVV~€P(S°™ù2Û¶sZc²,k!3ŸàǰáÇÌoޏyTXJgÞÌ©³ˆh»3"<˜ˆnpxvréçãSÊ:F3m9¥É˜ùiç4ÀÓ4›jPÔÎ4Í—¼Žªøäÿ‰‹‹;jHAÑã„&bHAÑmN˜â5â‰èÓ4?1M³“äNëô„Bú…™ÍÄdeŽYŒ*©•Ì|U x¹5W¢@ ðBNGÂû©“”R¯Ì›Kédæ¹®ãÎB´ï™yõø”²¨Î*ä–¥\ÇÌÚÖ™ß7MóŸùŒøøø†a¬dæ¿;—ʈ(#£°øòA«7l… 41C×mú|HAÑIí¢`fîÅÌ+ øPr§º×ë½UÞʾ͛9õ½(ŠùkÂŽI˜ˆ†µv1cÛö7Zë ”8—fæw·lùá¾Jæ¨rt1 !!!i©åÀ̯3óÛã’J.²¨ßÃÌ—!´5ÐËÌó½^oc"¯×;^)õ ªf–)¥Ž—­B4È(,CDgø ¡™¢7MÓœ™‚oÙ‚ND×»¬ª'£•Ž‘Yc'዇FX–5_ªPJ©óQµýêÈÜœé}c%}y3§n°Ô9 ƒWÀ2;a€;‰è…ñ)eQueš[–ò²“‡;úŸa#pËÃ0^&¢IEø ´‚½ßൿ—Z+D‹!E È~€w3w?·•Üi‚ž˜˜x€³œÓ`\\\TtdÖØ³™9ÛuiªmÛ³¥úü¿ß¿‚™¯GÕ–¶32GŒ¾3VÒGDù®ã+ÃÇK-ß4­õ©Ì|Mvré²[Ûí>$Ziœ´3q©Öº€_Ú‰ò°×ë­s†ÑÅ0ŒÅ.v.ý¨”:;£°øa©©B,pàI½ßÏ(,¾Àp2€ ÃXdšfGÉzôo±œ8Ã0n^Ѿ0oæÔs"†[FŽ;Tk]€*pïØ¶}T½ãõzï"¢{S à‚h¾.q“•9Æ*¿ÓG/µ|¿¯êþig\y¬õiDtÕä’¤…ÑJçØÄ݉è}"êà\ºÉ¶íÇjÙvx@¢sé­ë?þü×ø„6ƒ¤v ±H~ZêÏhë,`½Ô²¬Å’3-ÄBp¥Ëšz1 ÐZÏw‰ùÖ¸¸¸ÁRmöM ¸À"W=‹¥×o…˜ù ÷w¢ü¥–ï\"zÄy¯Q?ëã’J&f'—~—\Zàñx.ÑZÿ ÀoÎÇ3k³xÈ¿æˆy™'d÷1b™ŒÂâ7ôð3·gæwMÓ¼Vr¦zRRÒ~ÂÁ@*ÇŽ»åðH§adÖØáÎuN+”R—ìÞ½ûg©6µƒ™o@Õv¶CbÅï;3ÿç÷ Ôßjúο9ÀeD4!;¹ôuäÍM®ñ)eÑ("ºC)5@¼ÇãyF)u??BïÀŸß‹ïw2Mó^3x”3óeC×mš$µQh&¢¾áú?_à#3/0 c´äL3t'N´Ç9]~Øa‡Fü=,3ßïš!˜â÷û?‘*S'+}=€a®<¼î–‘ãNvºfΚöÀQÚ•í—TrBS¥);¹ô(fMD/ñ›O/.7–/ñ›´Ö—i­o0Ïùj ½€*ï|aâ½^ïÓÌ|'B¯Ò~SJ=tݦW¥& ͉ø„6ƒ2 ‹Ïtê¹0Õ4Í%gš± +¥.r¾éçgeŽ™‹*Çë-˺UªK½foÑsaM×Zω‘¤- Wµç®Øã c™ðÅRËw €ˆhMvré-M”žLï.ñ›‹Ü—Ú¬ñxX´ÛûS\\ÜÙ94*vÒ?ÚëõPìòg„jÄÅÅ9¤ h“Ô<¡ˆúD£Z?Ü0ŒGDÔ›™ ÇÇÇŸ`?çô§Y³§Fxvà¡pÞÑ¿mÛþŸT•úã÷û·¸Ý%ðc@ÐßuŸY›ß,ñ›O9ÛÉNºó ²ÓÇ&îìÞiÉN.= ÀgÅž¾óÑ®ø­J©«™9¡èU^"zØ0Œ—\êäë§CÖ~ÛnÐê ²ÎCh1dO#¢Ú-sƒ×ëƒfú»U ºRêtWçÑm #nu€ðì@%€ RMŽmÛ39Ú>+sÌ-ÑLϬÙÓW£*ªÙ}Lÿ‘µùÝò@›UoühæØ¢”úd\RÉU0ƒq€òÅåÆ^½,.7VÑD"Úá\:ÎsfþxHAQ/RžŽRÛ„h©?áÌi"âXê"êÍAÐ`á㈾TJÝézöS–eI5iˆÈí…-J»ÒvZm”ÒîZjùúÑËDôLvré I„Öú/jeU/µ|³ˆh)œE}áɃ¡ë6*5LhᢞODCš¡ºÑ0Œ’+Í@ЉèdWg·"RÏÍ1úDT¹w :áþ„F²¬y6‡­â¬Ì17Eyàø‰ëøäºþ~‰ß¼ Àt6DÔ•Rª|Ìï“ʼéÌ\î´•O3 ‹ÏÚ%´QÒ±Ô@¦ãÿ]ˆaAo ¼ÏÖš5{ú§HÜŒªiœWmÛÞ(U¤Ñ™î:ΊjåWÊm¡ŸTŸ{,µ|cеþÈø”²ëë9°ø @J­Óí¡ŽDt€¯Ùp€T)¡•‰ú<#œ¶3^ö©Ç° ›¦ÙÍÕÉ~á™+]ý©ó.=üîúØ7ê­´ÌÈ›òKTO¬ï}–Z¾Ìü<3Ï—TÒ­‚þ€ý —ò¹uxæ[Ìü^~z—¤V ­ŒÂâÙŽÿw˜âõz¯‘\ yžŠ)˜ù÷Qký€‘xnæˆÑWÑóÎéâD¦IyÀMÎÀ)Ú®tp(ót£ü¸evÂWõ¹É2;áš>¦¿P)Uçp¤¹e)_ŽK*Ù–y®¥–Z¾ZÿnI¹ï¬¾m¬øyéÏRPôT«ú1/½s*3 àh]øˆ(™™“œãD§?²‰¨ÀNföQ93o°À†¡ë6­“ÜŒ¨¥>a^zçç˜ùj"z">>þÛŠŠŠO[sžÄÅ`šNˆ†…ND×¹ÏxHšLÓ ”zZk}SõY‘(ñ…#è¨{õŒ^ `mvré9¥ÉÕ1OÞgæ:ÅcWê` 3çI­ªµxwp¦ÖúL":ÀQpöö;XŽPï°ÛY«°Ã©«µcæT"J@DÉáæ§¥jßøš™—z<ž¯Ý¸Br½IEýêü´Ô¥ú(¥^IHHèQ^^Þj·mÆ¢ »·}‰nÙòÃ}¨Zu­•RÏ‹ 7~¿ÿcÃ0Ф:[Øþ–7sêkQJÎW¨rÊrdCn´ÌNø¢éŸ ஺þVký ͪëï–Z¾×û˜þ×óÓRçeO•ÚU£ˆ_§µ>‹ˆÎ‚³u’ˆ~°Àû¾%¢o‰èÛòòòï˜Ú×æÞ¶m/NJJÊ&¢#•RG8’™'¢µÖ”Ÿ–Z `1B¾É_Ï(,–u9Ìõ^üÔ©'  S0|ÀiÅ>h}ÆR ¦©“ë¸8œ’ûðñá~Ù²¬ÍÒLš|FÄíí¢(&¥È•¦N ½Ù%fIã’Jê%*·,åE;û˜þ‘õÈË[ˆèöyé;JÍ 1·[§+òÓRæ§¥™ùi":À»Ì|…Çã9ز¬ƒ-ËhYÖ˲óûý8b^' ÃèVÚ¶ýŒßï¿Ëï÷_eYVW˲ˆˆÎ%¢G‰è`SlÌOKýjn·NVOŸt´”RãŸÐfÖúRåÒ½^ïã­5/bMÐãâófNý6ÕZŸ>fæ·¤‰DDÐÝù|N“âv‘Ú`AôÔ@x¿l]É0®®?Zâ7‹™9_k=±5שyé;å§¥æä§¥þDD/è `>õµ,ë˲†Û¶ýÒîÝ»ŠDzü~ÿû~¿¼ßï? ÀÑÌ<!“Ö-x¢p^zçæ¥w>Kzƒ†3ì³ÍDt‹Ó·\gšæõ­1bJÐMÓ<UÖ"ö„ˆÜ‚þ¡4ˆtv( ÏÊŒÌÛ)é`þCXÔŽtÏ|}woã{ëò»œÒä™~ëcúëüN|©å`àœ®Ó[[]šÓµcÏü´Ô7˜¹Àfþ€ˆÎµ,+Ų¬¡~¿q´ÓhYÖÛ¶ï±,« 3Ÿ@DyÌÜ™?ÌOKýl^zg‰Uß@†=ÁÌó68Û0Œ£DУkµäêˆÄ3³2Çtp˜sº³µ¯’Œ0K]ÇQ±TfΚæ^Õ~`cÜs™ð9€ïïîXº¡m`$€õM°ûÔ¹1+õ¨ÛÓaKgn·NÝæ¥w~Q)µÀÙf+¥Ž±mû:¿ßÿ~¬¦Û¶í/ü~ÿx˲D(Ê^[f^Ÿ–Z0·[§Ë¥[¨?C×m à‰^àkML ºÖz?WÇö[„n/[Ë¥ID”Å®²ïÅtØÎÿ‰§åôM°h+ý#"ªóß4¹$i1€™Zë—ú&؇Öå·ïþjàœ9];öjÉ•fëš•çÎíÖéI"*dæþ¦ÄÅÅu²,+³>ïÁ£l¹Ï´,ëp" ‰ˆþ3/½óš–^†M¬#×8Þ0ŒÉ"èѳÐ÷suˆÛ"ñLf>Áuü±4‡V>¥>qŸŤ¸çi­¿;Ãg­9ÃgÝЀº¼ ¡w¸u&§4y€ Zëÿl®õt±/‘xZ)uGK­3óÒ;ßðÖЫ_$¢«äþöÛo/[–•½k×®­Íùïòûýs-ËêBDƒ˜ù ¥ÔŠü´ÔGX7KOQ7†}¶y …w)0MóìÖò·ÇÔ¶5fîàê·Gè±ÇºŽ¿–æÑNl¨aá²?&ŠIÙ†ªÅ˜C•R>­õ¹}Lÿožeæ¹ÎTzmýf>®¾ ê7Ò7|Ñ ÿëç´µ+–øÍº´¡Ç‰¨0?-õÆŒÂâÇZJ]™Óµcº³büq„¶šeZ–õM l À4Í©²æu?r;]7¤ èié1jÏ‚¢‰s»uZID½™y.€ìÚÛo Ãè   ¡µî ”JaævN{ÞÁÌ[™y‹RªÀ²¬âXü»cJЉ(‘™ÃÇ¥z¬{ûÈzi ecï Í-#ÇöðŒÜ-QHÇïuÍãñ$MÚ™¸,|ž\:Ðq:TÐÇôÍÌ ÞüÉ—ÒîÚ‡°þÀœÐv×á“v&Öy¸ÿ}ñë—Ì´ÕZ/îcú?WJ]´¸Üø®mh B3oå§¥V ôé¿…Å3›±U~Rj5BûÅÏuÞÓ’†eYcLÓì`3?Ÿ–úÆ5‹Ö¬5Û¶»KºZëÉõÖèhÆ4Û¶‡»?ï®”:Àéο¿8mD„°…¯9÷3Ã0Œ_™ù "ú¯mÛo#«=êÄÚ¶5oø@kmGè™ÿ+À—Ò "ÎzW£‰–•n»Ò`¸?È)M~}rIÒßOD¯ÑÈ‹±nécú?Í»{n‰—Z¾ð–ËÃꛨI;PJ €µÖ+ÏðY{]8è¤ç<eá> @?3óÓRw秥Ι߽ËÍ¥bT”ï^Ÿ–ú?fžÈÌÏY–uL,/vkQßhYÖ„Ü$Ÿól¿“®ÓµcOé2jGFañz"ÊqN‡†qaG™¦ya•Rk uYXÌ]ü†”ð¿-*]ŸïODƒ¼aÆÓ4‡ â³ÇÚ”»×5º 4õó2GŒ>–ˆÂÓìßH•fñ2ÿ6‹œ™;G)= ºKØxŒK*¹„ˆ†+¥>ícú ÌYjù©ÁBØ¡µNiHÂÂÖýø”²™ùƒÓòyïoóyœ÷å+õ0€ÿå”&ÿ5;¹ô€§Ì|™mÛÿm­mIJ¬G Ã8–ˆ^RJ}2/½ó=C Š&Jï±o†Ý•Ÿ–Zìôí/ð¹-oDz.ð!3¯`æâ’5ùkš5ܯúýÚöz¢Rª73_à´­™9ß0Œ!DteYßEëï5ׯޚ:Ù¦Âãñ´ÓZ‡;ò_DУ0E¤ÔÖpcæöQJ†]Ó rOä–¥¼âˆlGC™ùŽ>¦7€J©Éá©qfÞÁÌÉ‘ÀÉ%IWŽK*¹˜ˆrÏÛÏ:@ŽRêNèÕÙù~ÆÅÅ]`kNiò{áߎMÜÙƒˆF: É|Ì|g~ZêwJ©«¯Ýs;;æ¥wÎð&€ÌüwÛ¶?oííĶí¯Àçó½ÈÌçvëôìÐu›$ÂX- ¢!Ìüª¶°UxŸ™ŸùqɬóÚ$˜ƒt¯Í½v®žû{]ܶs׽Ϲ¥’ˆÆ;æÓ˜yE||üÀŠŠŠUQéOc-ïÝÆ[S?Lkä*ô2©ú‘Gk]æ÷¤hMì¡îKd7O.Iº#§4ù`f ›ÖzCÓ?ý Ÿu€C”RÉ•Èܲ”WsJ“dæaDÔQký<€ÕÌ<ÀŠßjsÓƒÛþ´Ú{Ê®¶«sËR®#¢3|æ\>BkýÁ¼ôÎ1åQkn·N·:‹˜Þ¶,ëó?â÷û¯0†ˆ®ÎOK}Or¤VVú"áÛãñôÞ±jÎ…;WÏ}ÖózÑ¡mâ];WϽW)u€EÎå=Ï;†aDŵo¬Yè=XëMÓ‹3'ýÞ‹ Ggô\ê.(%Ãp¥§^k7rËRþã’JºÑÍÌüBÓÅ)ØÜ²”—ª]j`Œóoo%N'ÑLf~2?-õ¦ŒÂâGc@ÌŸt¶=fYÖÒ:jƲ¬©†a\LD/ä§¥~¡”ºpðÚ[$göb¹*5JkÝ@B0lÔ×MÛV>þ´ë9ìÿrfßp¬þˆêJ¬íC¯ÓÔg#r¢ëy»¤ÚG¥ÌËbLÐôª'·,e]Niòp":–ˆPJ-‹µ<Ï-K™` B«û€Gæ¥wŽj\úyéŸ#¢˜*b¾olÛ~À¥R™–N’+{Æð„­·ï5¼_c?cǪ9ÿbæ§œÓ.†a<ñK+·ÐÛ¸:òÝRí#Öz·« ÚD)Þ†Zè5XÛ'—$Ý>igâ'±˜ï9¥Éoj­ÏrD˜ùñyéψ’eþ 3_ÅÌ,Ë#­¢Ö–ú[J©sˆh?f^¸ ÇQI®ì™ŒÂâ1~qN›$ÔðÎÕs¯°Ö9½ÙëõÉ¿1Ö\¿–G²s¯fy¥ÊG×:ff;Jiø}¦¦²²²¼µdü”]mWÑ¥Î@ÚËÌÿÞºf幑LC~ZêãDt-€lÛ¶'Is¨åååk­ÏС²²òƒŠòÝ $WöʽN_sRÛC¯l"‚ÐêyED#Z­ +¥¶¹:÷ý"0€(s‰{’ÔõÈãÎ÷(.Lü½®ÅÅÅýÖšòrIÒB"ú—sú—·†^=7˜bYV®´†úaÛö:"º@ç§Oëz¬äÈ^­ôÙ6;ýÍ=MñŒ’5ù…Dô¢sz" &Ö,ôm®Î½CÄ$Þß¶vÜù-Aÿ½®MÚ™ø]k+€É%Is‰è9§ü}n·N—F@Ìo`æ;ˆè˲ÆJ3h~¿ÿ#"ÆÌ=óÓR_•Ùk¿¿sxLû^Ç6Ñ3Â~) Ãè×*ÝãñüV“ÕÔ„»kÂ"DÁBgæh zB¸_l­å0¹$éj?9e2¥)Ÿ5¿{—S˜y€ü~ÿ?¤4š¨? ÀÅóÒ;ß#9R3C ŠæÂñPÉÌMâJwÛÊÇ—†Û":Ö,ôÿs6ù`0è©ê‘‡™ÛºÄ=₞•9¦‹ëô×V>¸ºÝ9LÛ­Óø¦xFÙ[2´ÖÿPlYÖ9Ò˲r™y¾ã<èÉ‘}Z釷ï5|P=#)1­U ºmÛß¡ÊÉýáMý¼Y³§¯uv–jŽt‰ûwQhØîí>›ZsAL.Išg….nŠg¼4 ïåÚ3óRõ›¬ŒPP’§Ÿ<éÈC$Gj´ÒŸð­Ó½o8´}¯áϵë9¬¼]Ïah×sØ–v=‡e7ÀXYãF,FE¬m[³„=]Å̉½•a«¬Ïç;\ªzÄq{TŠF­N®¸IŠá©Ú¿ä§¥ÞÔ˜7ÎOKàB£mÛ^'Yݤ\ ÀƒÏIVìq0ÿ sxb»žÃ2µÖk™ùjT½‚; @N»žÃ^¯§ ‡ýìßZ½º•‰ÈPîh_GK58á2®œ9kÚWQx~G×ñæÖ^9¥É¯Øàœ6š ÏíÖé8÷øeYJµoZ,ËÚ@D™úæ§¥Ž’©ÑJŸ6 Xìá«Û÷>¬ÎâªTxM˜m‹V1˜ÏÅ.ÄŒoDУƒ×ë=ÑU£b»C¶*¥Š¤TyÎÿ]+\'ÍPúÛo¿‰§á÷ûxÀ½ó»w9Tr¤Æöÿ°S?Söñ½ú‰x8Õ˜t"rOÅu‹À#7DøyBÍù½!ÚiÐZ&E<økÒßXÎ çŸ ½ß¼ôÎCœIDcIGޏ¸¸`­õLÉ?3tݦIjãö»c= á©û"=4V-ôu® éÚäðG_ÛgK(g»Ê:Z>ÏÃ!s¹e)ò^@œA}‰(¼—y`Cîå¬jÏ𑳭Jˆ »víú¡W—ä§¥þUr¤F#òßµøÚõ¸uxV$bΪbNÐ=Ï®Œ>¾©Ÿ÷ðŒÜòg ©¦iJƒÈ5$· éçß2rÜɨšÛ %R…Ö:Ñí°9];¦×÷>/ èÛˆÚj­GK®F˲rœú=Mr£FKzr-¾öb=nÝ%Ò}KÌ zyyùO¨Zéž’9bô x¬Û:@€ ×ëý¨U ºRj…«6¹ Ïš=} €ð»û¶^¯÷j©âM‡ãÀ'üþ¼ò˜cŽ~+ÒiÈÊs4ªö–ä–¥|.%ó§väÄÍë~dÂ@j­GÐJ©ÉÉØÀï÷¿`93‹•^ó€g¾sxÁÖ_K´€“™‡„•]»vEÌ¥´ŠÑŠ·@x¿jjV昣"`<ë*Øë¤z7Á`ðWÝ{ï_7_…d¸§‘[Ät{vréQcwöh¬ûM.Iú@¥Ó>j}_kçŽ{ eæçÊËË·H˜9@ç¹Ý:‰ëÝ?çÍS€7íâ[ê{Ÿö½†÷ƒó:™ŸŒè ZëðªùoÀ¿#ù7¨Îß·]Ç6õÞ‘ûB‹FÀSYY)Ó4û{,›9kÚóQJJ_×ãíæ–Úî:5;¹t€GBýßÇÌW3óUDtŸŠvZvréÏãSÊÕìcçÿ“êð›Á>¶mûk©ñ1É|ýæ¥w–€T5ŒG>¡wû^ÃSëúã¶=†ÞED½AÁD—‘غˆÞuö‰Ä3™ùq×éH©ÛM’ÇwºN£bgeŽÀ ¢'—$5« ,ã’J®ÑZ HDç,µ|éËì„»—Ù Ï-³ž_â7'.µ|ý˜¹ýÈÌïK*™XÏvŽHxð‚Gí3¤ñœ®Óò¾÷´ÔöØÄãñ<2$õµ’äú?ïW7ÒZ_YG1¿„ˆÂñÕß7VbVÐ-ËÚgº€92kl“¿×ž9kÚ ¾t:²^¯÷v©âjŸ l-”REiPá~øj3óˉè«ÞøÑ|n‰ßÜãªSYû IDATevÂçKüfÑ=ã’Jn«‡ ÿ¾¾¡²²rŸq•R×°KJJ®’›ìÞ½ûGÉZ¡?ŸÐfœ]7J©Ëë(æ/"´²}«Rjx4ÒËSî`æç\Çé ˆÈýÞO¼[5nyÞá:}êá¹QY0ED‡µÖÍ&¼ä¸¤’c‰h>­^jùú¤tøÝØ+K-ßOÑãSÊêÔ‰ßÿK›]§µ ^t-€7 Ãè+5>vq…]æwïrªäÆŸòæ¿NuRm¦ÝÛ÷þ ½ŒPDµr­õ@¿ßÿC4Ò®šCÆ:DÄÏúŒ¼) l+½Ñ­ó¾Î ë(€)ÑHGæˆÑ—HtN‹¦ìjÛl<˜)¥žf怿×õ·K-ßpÇòÈ»­}ùµý]œA}„·ÝtÙÛwçwïr€¿0ó‹Rãc¿ßÿ$€3ÿMrãt½~Ø2§3¨é;zßpx»žÃF¶ë9l‹ã@‰ü¢”:§¢¢bUÔúˆXÎXÛ¶7ÑçÔÈÊ3"BžäTÜæóùŽjÞ`ë|–SéAD/æÍœúM”‰ƒ]ÇÍFxÆ%•\ÊÌ=”R–øÍÍõ¼ÍMÌlVTTÔu0žv?ro_ ƒç–——O–ß,øU¯À‡£&¬'¢ÕÎéƒízÛܶÇÐíz[Ø®ç°åízÛ µþÀ ‡9ß{[)ÕËï÷ÕA3‚¹®Ó#ñ̼™SgѧÎi =Ø0 ÃÈp¢sZâñx¢ò*ã–‘ãŽ@ÕH `nS>oBÛ]6ID÷ذÄoΩï=–Z¾Ì<™ˆ®—Tr|Ú`8¸Dç}¤ñL"*ðx<›*O}>_çÄÄÄ¥U5Jßú!3§Å’Dký†s˜ #ì ~Nö‹Â>0жíþ~¿ÿûh§;æݶíçPådæ„[FŽ;%Ï ƒ7£jËÁ@Ã0$ô`ý:àC¸ßõÞ=mú䟣‘–`08ÈUçM.I*nB‹zžÖzv# Np"Mjè½ÞßæëÀ"¢ºéÏìK¨Oįy†aoÆm¦i.3Ms·iš`æ¢ÊÊÊ­¦iV˜¦ù­iš³LÓ¼PZZÝñx<ðÌïyôÃ’츽W¾‡¯Š…Ñónp¤mÛgÛ¶ý¿XI·jy[ÊÌ/»:刬œ5{ú*f^à¾`Tõ:tCÕ;ë¯òfN­´T›n² Åü"àÉFÊÃ+ØKüæ¼°i3? Ö‹ã”RáÅ‹qóÒ;×(êóÒ;ŸÀp,–† ù ¦i.$¢/‰èA„|î·©öµx„^Œð¶išß†!ÞÏê@yyù2eD$&«1ì³Í«QÇüÛ¶/²mû\۶ϵ,ëjÛ¶³m»(ÖÒÝçqWG|õèQã#2å–=~ô®B=Â0ŒvRÕk×ë×7ý+ZiÉ1ú œ:´}rIÒSMñœí›uÍ`Mܔԡ‘n{™c%4V{š i\RÉŵüÉWû;xƒŽÓTX–õNÅü"Z…º¿Û=’ˆ^2 Cö¿×!?Ípp¢~Í%ÁÍBÐý~ÿrT…Ø4‚Á`V$ž{Øa‡ÞIDWÀ5õ.«Þk‡išçÑC.!¸FÞ”EQ«èJw‰Ïÿ³wåáMëûý&mÎI—”íº/P\¡ ‹ eQqAQ\~*nÜhA \®¨W¥- ¶àÅ÷Qq *‹Ð¢W¡E½(÷Š@Ó%ç¤É|¿?2‘XÛÒ´i›¶ç}ž<ÉINæÌùÎ̼óÍò~9uY=ÊF8Ž™_­ã¶²Úðá}•Ý¿J!7«*´|åuõ̘ùÏ-8>Ÿï¨F?N°£A£ǃj;•££07išöeqqqŠU ëd¯ÍN±,Q-Ê¡§ÇÇÇw²=Œ`æÇƒ>kªë>‘3ûsU€™‡ÃêÑÖ‚ØØØ#˜ùÙ òµö‰œÙÿl®üLš˜>€™Ò¥FttôÓqôø’çà_4ó—Q¥‘oŽq¬*wᎷ@=ËåÜTþ®á´î8¸¾>Àtfþ'ü;!¼Ì¼þ9Ë_ëARgŸtÒI¡× ÛhyI‰'X¦ø+¤”Å‚dšf‹hó[ ¡{<ž·æ,þ‘–:ejS];'wΔ á—h)å²èèèÞV‘¯K¼^ïgŽQÇ{¢¢¢®jæŠ,{úü#ûbŸ;ÃéZHDW(úvÖ¸°DpcæÓ ç-¬uU k‡â™•¨ÿÕ$ÿzb°ª\ˆd>ÀŸ-¤”ý=Ï™jÎòx"zþÅ!‘º5ü^';m êY¸-»6p);Õò,Z}}DôPPC7£‰ þÍ8(°áB¼gíOÿ;4M;@`K”@ÊÜÇ2÷4W~&¦M=mL!ÄÃà™?àZ" À3a,wT&%ÓŠ0gûgQÓÛ•SÇó÷ªÎÑ߆ܕw§5ÀCZµEeDtQee妠߼†aÜ ·¶»ÉápXû¬kÁÿþ÷¿[ÕÇ“,kT‹€HL_‹Ðà Ã0^ðƒª¬S'LžÙT×~"gv±”r€RõÕ1RʉÈÚ;Hæo87Ðç"¢Ûsrç4khR)åCA‡‹fˆû)liûxW†ÓµB1\q‘”òHqY.ç¼p]ƒ™uF¸íÂÌ»å¸Žäø‡z?¬šŸ»WñöBñÎÓ­òôšaÕn%ŒŠŠz$ ÏßBÄÅÅR5ËC¯ê½wKàKÑÂŒëSÛX̤'ç/h²‚8oþc„âà¾ø“ìvû=š¦µùù'MÓÞÌ í‰œÙ š3Oi©S.#¢€¸‹!„›‚Y†ÓÕ}zûR@f>ËívED·XæÛИa¯øB<îuììSïÎj~ÌóïªÏc ú\cÔ»òòòÿÕ»P¼ô³ìv{?‹—jµÑ.T<³ðWÛªNMÓ">ÜlK#t†ñ €ïÔa»­[·¥7åõ"ûK#xÕW‰òív{Ï6Læ« ïdåäΉu½¹AŸŸžu .,JNéñ%7Øà·Í l?厭v»ý$ƒ¥”Ï„ù$üœÂí¡ÛUƒU×΂[_¡Ç+ÒÝb¹é¿êÃר`ÆÄĉ¿ïE¯l6Û5ÕZJ8-KTSù¤,²Sį›-ÐÆ>"J :Ni*õ¸rrç¼ÇÌ£à› GÑŠèèè3ÚXï5E×õõ›''wNFsç-uÂä‘Åž,—sR˜Èü9"zŽ™gåŽ!e7ÛÏS¶˜@D›f—µ[æÆv7€vЈ;T¾Ýu|Ö²WÍoñªã*†UIçZÕ1ú{¥÷ùþÙ€{fQS­( tÊ,üc ‹·Úy!Äi¡7Ž—¾@@k—|>_“«åΛûü¢)9õÄ«t]Õ zttt»Ý~/3÷;ØnòôœÜ9iÍ·IÓ#¢àNÅ# M3Ãé–átýBDg !¬1cª.®°†pc7$ëî°N-1s,x½Þ:º”2àÉÛªI+õï\å+½ïp8Î úN×u}€[p»],jªµ,X„^;± "~hÁFž  R5 g¦¥N¹³©3“;çf> •´â˜y‰¦iohµt]@ñePCi0óU¹óæÎŠ„üI)çy’?d¹œõ’™Þ®ì˜i ¥/xÀëù†£Ûª íëàs’uwWc¨Ï !Óá^§q®køX!D`ØŸªù9>¨c ªÛw‚”2_×õ š¦­Ð4ífžVÃuë k8¹öŽ•Eèµc§êøDüN€Kè¦incæ`/){bÚÔĦÎGî¼¹ßÁ/ÐQôõš¦ݧ5•êØØØ#5M[ÎÌ÷yj¿ÑðÜysߊ„<¦N˜<ÀeêÐ'„]ß´ÒãK¦K)·è&„o8jŠ7A…‡ ;2KâWÃ?=8Ì…¨eZ5^\LM„NDqõ$ôšæÄ‰™ûÀ/ÿ…®3fX«¸k&t—Eèµ¢8¨SќՒ­ìñx25M»Ny1ÌÜ,?rrç쀉iS3•7Eìvû •••+™9¯¥{å^¯w'€˜ ¯W¤gÜõõ±Ç³"RòIDs‚ófˆû:Ô42œ®Kº j}– jÄ.õîp8ÇDB˜ÔVç¡+Bˆ[pPkýò´Ô)Í6‡ûDÎìiÌ|€ù…nÒ4-[×õ[[¢ÇÙš¦mQ^y Q—Dô@NÇ{Ì}‘’×´Ô)Ë„O~Ér9CŠÌ75î@ï §ë#ïX–o8Sd^[b€ç´Xj´µÌü18Öst8Ò Uœ¢:Á¡tÄþ¡ò²¿šüÕkÈ–ˆšJpè¿'ÕZ¾œõai+Ød«Î‘œÑ–Nèp»ÝëàmÀ¬Ô “›-Ø@î¼¹o3óÙšCgæÌüŒ¦i«5Mk±›ív{»Ýþ’”r5€U ÷¥Oä̾?’ò;1mêxy·‡à‘Ÿ8-¡t©b#€ fî™o8¹*>Yw_  G•Q°CEGƒ”òö°Tz!FðÞöaìÖþØk~ šßêEèÌüUÚ| µ=‡ú®h+öùÓ#—RÉy­Áà¦iN°Eƨô͆Üys¿ÏÉ3þ¹ÜàyÊ–kš¶SÓ´I‘hKMÓ.Ó4m-m!¢ppZÆ`RN9¹s>Œ¤<§N˜|Zpðó³\Îõ¿íËÎpºžð=3·—RöÏ7W®1c¾¯cE¿—™_Ê,‰oÔ¸Èjÿürf¦êfù‰l¡xèÕ­†0KQ=â¦i¾ ¢±Ë‡”ò]‹–jíðX„^ ¢¢¢öÙêH‹Ðü1£…²çÄ´©K›;S9¹sÞËÉÓÀl(a…®×4í]×±Ûí'7g>m6ÛPMÓ&iš¶ À{P¼Ý7‰¨gNîœÇ#´AZ5@Dë³\Î µŸ_rZz|ÉbŸÏWà4":?ßp\¸Ö»¡®×LÖÝQß`åÂF¾ÇÇ5@«hPøÞdÝ=þp¬uÞ‘_ÒçÉ·WÓA(k@[òQ#›®ü?þjÑR­°½ÜòÍ»ƒê¡å¡7‘—¾ƒˆ¦52×¥¥N¹#ò–“;gjTTT"€9øë¢¢c™ùn"úÁn·¯Ó4m‚®ëM"/HD)v»}¤¦iïEEE}ˆØ`%$ID¯3sRNžÈ™]‰Ï=-uÊ;Š  LJY£;-¡th†Óõ‘}8Œˆ.Ì7V»õúl1¼—ˆ^Ër9›d±UfIüçDô*Ý—¬»»Õ' w/p€/²\ÎëÜH,ZÝý–@lll}æøïÆAÕŰƒ™³”^¹…šÛ‚‹Ð‰ ò‡Ü£Z“Å ÃX`·ÛÏ"¢[ÔWOL›:ð‰œÙ«›;osËü-ðybÚÔGÜÎÌíƒ*Uý™š¦øŒ™?µÙl_»ÝîŸÂPiSìvû/ðo:ÀÂ:tf~À#9¹sþ àÿ"õy§N˜üOZ3ÐxË.Møî/½r^rß‘¥Œð3¿àô5f̿᪗;À»RÊðË7Mï[ˆ»|>ßE^­Ïÿ/èdtÐAJ9À¦H±wP9ÚRÍ)?©Æ®k=êìV]×ó4F£=¦i>`qÑ!Ñ£ŠY¨À?›ѳVhøh»Ý¾2((‡‹™“sçÍ-Œ@Bºšˆn‚1—½¶vÀŽÀ‹™÷ !öK)K‰ÈED&3Û˜Ù)„hÇÌNUðNp"3Ÿ€Cȇ2óWDôbNîœù-á!§¥N¹ÀSA_eg¹œêúg8]—Ñ(f¾ þ½òÏ !ž\Ynÿµ¡×NÖÝ[™ycviÂM}ßN×0ïø4ßp\BžgÃ/Æt{–ËùTˆ×üÀi~I)(ú[Žºvaæb"çv»Öç¾ÇfgˆJS1¸¢¢ÂZW òóó‡:t€I)E[©yI‰…zøÄ4Í -BoBèºÞE­ =\}µ3=ã®—#i‹U5uüÚÖƒÐ4"•Ö1óÇD´4'wÎ-åùNL›:˜™?†?7|nšæMÓÎR6¼þE\ïXœo8¶ˆ/Yw§xTÑ}Ö¸ÿ4ÇýOK(½‰™Ÿ'¢Ì|S¾á¨uµú@‡±”™¯ðt–Ëy[ˆd~€­ªÓ·dlañ¨¼ O†1µž„žX¥Î6LDãëÛ¹hKˆ‰‰I–Ræ¸8¥ h¹e‘걨g—uju­iš"5ŸQ­Ñø†aGGGB|ÿêÛ®³³¿ð‰œÙ›çœÜ9O>Oš˜>ÀçóGDƒœ¦FÎEDÛ™9Àg9¹s>0@½ZLÌèÔ “{ !VÈœˆ~ef¦i{Dø@Ök¯LÈÃ_Cº6œ)üûñg5™@fIü N×Å̼À&å}/ªJìÆxfžàfžž]šPiÞài‰ðkÖW‡ºÕ÷žÜnwQllìÑ^¯÷ "ªw°%"ÚÏÌ×»Ýn‹œêVž±ì·ZÖ¨­“˜HÎ$µæ' ëúfþj8›™¿Î7÷Ì–x//àDÂ2žˆÚ©Àvø±•0³ @™ÚJ´ À6›mÛÜÇ2÷´ôç™:aò©D´ •Ý `3´ÆŒiTùÙdÝà²,—³s$ØcFûò£¤”s˜ùø¥xÿKDÿQ2­ÝؘybFfIüçõ¹F†Óµ@/®”‚¢5Ñ÷êú3÷4 £{êí\Pû4TuXÉÌwš¦ùo‹êlë,R Šb,kÔŒ¼¤ÄÕðo;ÞfšfÄjºS(°£•ìjà^?ÈÉs©UD[&ML?N ¯:g!®[íÖ›DvvPŒy–”r 3_—]šðz$Ùfz»²c¤”W0s_!„CEFÛÎÌÌ.k·©¾éNK(ÂÌ_¨Ã)E5*ï-êÙe&ÝcFt˜êí Ì|PÛB$©Fe² ðö›‡nç–RPÔ˲F­„þü1;¶š¦yr¤æ3ªµ?Ã0žÕu™y±"õKÓR§¬ÌÉ3Ø*¦-Æ3?ˆÖAEç"¢}ÌÜ0Œ÷ÃPow‘Î0f.¥,BìPd³Ù–•••íêÈBèH†q©Eèµ#°µ²2’3)Ú“0 ã9f¾;è«Ái©S¾°Êh‹ óÓˆèc µY`Ä3¦ÉÈMKrUl#©&ßËÌÏâà*ç=RÊ¡k=±›š2ɺ{€Ø‡ÿwt[±ýŒöåG1ókêpkJAQj]ÿk³ÙV “N:©Ì*Å-ý„«,3Ô qê}ŸEè‘GêÏÑ…8·¼€'¦MÍ´Êmó"-uÊ2"úîJØd³Ùz¯õÄ41™Ï0DJ9ßë–5BòÐ÷Z„™¤þ93Ÿÿ^m fž––:ÅšOj¯ü´´Ô)?¸$èëò Gïpȵ†‚ZÅuî"¢´Ùeí6·•g_rý©Ó ¥ 9 1½ࢸ¸¸#¬RÑu€oÇmٵѲFÊu “û‡Eè Çó½b3¯à½4-uÊ–Ô “O·Šq“‘ùuBˆ|'}ýt¾áhr½€c -&¢ÌÌ’øEmåd8]“‰(X1pûØÂâïCMGJù€(¯×{U²#Ö;O0@*²p<Ûû„cpPœÌòÐ#n·{·Çã@DÁÃí=ˆhóÄ´©V¤¦FFZê”·ˆhiPä¹rf™o8nkê¼ tç)eÁ¼Ì’øémÈ3¿@vÀySïõÚK>¶°ø{"Ú` »G.¤”×QQQ/XÖ¨Žû“0…øŸE葯aÓ‰h,·ú.Š™ïOKòÙÄ´©],…Ó¦MKò€+ƒ¾.–RZcƼÒÔùè0†0óÛDôN–Ë™ÚVžCz|ÉUj˜ÜFD» Ö.Ñ{õM“™_bæ>š¦l•ôÈÝ`õ-ßü¸Û²Æ¡áóù:î°½…À0Œ<":ÀÚ ¯ÏcæR'LžnY(ldþ:3¯€’qUÈÉ7]Özb›|No€V1‚™—3ó™%ñ×·•çátM%¢7Ø™ùkf.P þ~%S/ܰò›ð‹ÌdX¥=² iÚ'3óS–5êŒ? Ýf³EtTJ²žUMå^{ÀÄ*ž5RÊ óæ?V`™(t¤N˜| ÍpLÐ×."J]íÖ—4Gž:ŒLfžLD2KâŸiCžù}Dô jÖš¦9B×õmÌ܈–ŽÙ¼óº†¤¿¸W×YÌœàdÃ0¶[¥?2 ëz![JAÑi–5ꆼ¤Ä§Ü Ü4Íxœ–²<ôÓ4ÍÉRÊ3ˆhC°3'„Ø<1mêë“&¦o™©n˜41}ÀÄ´©‰èµ`2g梢¢º5™'ëî·˜ùvf¾¦­¹ôñ® §ëµŽˆèå,—óœèèèDfî NkpÀ›7{€ ݪCæ—èÉ̳,kÔÌœ¤>þÉdnyèuƒÝn·O#¢»èAß—0óœÜys´LT=ÒR§œÈÌ÷ªRÁÇÝÌœÚØáNkÂ9öò^Bˆ¥ð¯—¸&ËåŒrjÜþ6›íTE¬>øƒlxtlX¶íe8]'x@`Gn`½@z|ÉÝDô!޽iÇOaðlrÜFD'ºÝî]Vh^8ŽõÌÜ>¥ èDË!•c7-5 c¤Eè­£w›ÈÌÙøë".Ñ~)åc±ÿ…È»1ó=jñMpD?ƒˆæ®vëw7WÞ:Œ˜ùn"š›Y?-H¼ÝEDWˆ…_ì( Fu¤êDÑÓ EH/¹–ˆž_D©@Z–ËùTÙà<ÛS Šº…ãÞ÷êšÈÌÛ,0 ã«f4kûu1€åîL)(šoY¤nXØ£so!D@¡ò.Ó4·½uUŒ!Ìü€žU~rÈ#¢œ'rf·EÛLš˜>@J9Àˆ*9x“ˆÒW»õf±Í ó8)åÓz3ómÙ¥ ï4§­¤wMkçZ­:=0óóó>u¸’’iE•È f¾À \Ì|Wvi›!zæyúê¿ѵ™%ñŸW9ÇP‡'S ŠÂF¾yI‰óÜ&¥ìãñx6[-H³µ[Ûå‡Ü)½•™Ÿ!ÄÙn·û+‹Ð[„Ýn¿–ˆf¨êÍx‰èmO=‘3ûó¶`Œ‰iSo’RN ¢þÕüü±”ò¾µžØõÍì•§x1ËåßÜöJ/9•ˆ–Ñ>Ÿo\]4ê=&¯:/ÁØ À»Y.çU‡úÏôveçH)_ÆÁÝŸÜöalvâÛ_: ÓJ3óJ`ækÆ¿ÎûÍKJÜMD{Ün÷VÓÑôÐ4m&ÝODƒ²{¡-"/)q!€q<¦i&0,Bo½ˆÒuýffžà„j~ß`QTTÔósËlUq›S'LîADc‰è¦ Q˜`|ND3›2Ôi5^ù™ÌœÃÌáb^dÞƒˆ>ðF}Äsα—' !ˆeæ+²KªUs›–Pú3ßÿ”‡™ïÉ.M˜UCžîUúùè9j|÷>wMßÎ{^Ô³ËuD´Àm†aN)(ºÜ²H½Êër(5M³EèwXeeåU®0 r‡*4ß3ó¿| `+€¹óæ~Î,žùÍ̼À»)E#,‹ÔKút;ÂëõþG9¼o›¦yeKÉ»å¡GJ ÃX  êü¢]Ó´ÎBˆŽRÊvÌÜžˆÚ3sü±¬ÿ@B<åóù~VÞÐ~)å~": ¥Üõ_µ(«»z]©† UœNDw(°ƒˆ¦d¹œ ´2¿»CÅ̼‡™ÜY*Èç¡Îw?@Dã•ç¿^q…Ïç»Oò;€Ô5½ôø’Sˆè{•V“ª¸Ù¼ó™¼¤ÄTfÎÕu}¡a㬪_hšv5½ ÿ"8‹Ì¯×;2Àj×H‹Eè-Ó4·×R¡ß° ù|¾ŸÖ˜1™-õFh×ÑDTà}"ºT-òZØÒ²²A±·!é Š1—R.'¢SÔWo†q%€Ýš¦}¤Hù§P@¯ÀŸÏ×äÛ'S Šr÷êšÉÌÓÇ¿Ün÷½V5¯—g>ˆ>aæuc ‹/²,Ò`töz<že-)ãVøÔVÓ4·3sjØomiùè0:t%ëî=Bˆ§|CD'f¹œ—×uÅv$‚ˆdàcCl#¥\ ¿bуU†»(o"¤iBˆ@ÔµÊq[v5‹˜Í;§1ó‹Ì|¦iÓ­šbbbÎð€ÝÑÑÑWYiöèÜþ‘Nx-`«šEè­DÕLè0†´"•¬»?gæ"fBDÓ2KâÛg¹œwe–ÄïléÏ$ËåÜ €¥”Îúü€Vq 3­H»’™o2 ãŸUN ¨ö…DèÌ|²úجv[X|#½BD³t]ŸcÕäºA×õaRÊO™y/?jãöß,«4 6›-°ÎÅGD-®,Z„Þº¼ô×á_ð¯D0‰ U<›¬»K¥”s2óiY.ç€Ì’ø%­ðÑ« ¡’ù©J÷ý¥Dt±Çãy±Š‡vü‹Ú¿¸L(ì]ßÖܳyçõDô€)J®ÔB-p8·xÀ¦[ÖoýmÌæÅ–U†Å½ºvfæ«ÕỆa´8›Z„Þº°ˆÞ)eÇdÝýï£K$dì{yÿcV²î.‚ˆP™]šÐ1Ë弫¦ #­ˉhp(HÖÝ'ÑGŠÌË„Ã Ãø´/»sà3…J艑BèŠÔïWŽFèºþ¾Ukô̧0óÓ>K)(` DŽ̜µ®L1·%ÞƒEè­¯P.Q{43{˜yý­âšæÈK²î¾$Yw?•¬»wÛl¶åRÊÃLÈ,‰oŸ]š0*Ëåü -<)å³ÌÜoPŒ™\—óŘÇXàXåBˆ Ünwµ:ú>Ÿ/˜Ðë¼Ï=ÃéêÀ®þ·-Rl5¶°øQ" àb]×·jšvºU«ÿ♿ `3¿œRPte‘ðö)U}Øèv»×¶Ä°ö¡·BhšVÿöO™y…ŠÛþ=¸Ú­¯lD/ü §ë±ù†ãk’u÷&½áŸ>nšf¶³Ûí÷ÑÃÜY.§#„ü ‡ÈBˆsFoÚQ ØÂÏB¼ª:5Ó Ãx¼-×阘˜s¤”Kt$¢ÛÇlÞ¹†¼¤ÄtÙÊ)áñxÞµÝB¤úæÃ¯ v²ÂíóùfÑ v0óË6›mõª -¿Þ÷‰Ì|*€Dt€³ˆÈÇÌ+|à£,—³ÙÃÄf8]—¸@?§¢ö­š>?0ó˳K^ W>¦%”&ø†™×m-Yw¿ à*å)Œ1 cqméÚíö‰èF[²\Î!Ø% @DEE1jãö=‘VŽ++Ê—<öé gæLÓ¼©-Ög]×oð€_¥”׎۲kƒÕÊ…>gÕsýNê  ­7 £K½‹Ð['b5MÛ àp"z%³$~$à9ñz½W(ú<øƒÄl`æBˆŸì–RîNHá”RMDGÀ–óX'«ÿ~ÏÌDT ¥\3»¬]D44ÓÛ•+¥œàz‡øÀ:fÞHD…Bˆý>Ÿï€ÂRj6›­ë9ˆú*ò?NÅ_JD³Ã±â~ZBéù̼Œ™ÿµÆŒy¨2_`œ"óLÃ0¦Õ¡óðæßÈr9¯Ðg˜ ÀH)(Ò#¹0/îÕu3?ÀEDÓÜnw›ðNív{›Í6Ÿ™ûX”RP4ÖjÚ¥|ý‹™—𦹬¥Þ‹Eè­×K¿À“ðné7»¬ÝÆjˆï,f> @7­Ë ÀAD:ü4‰¨ŒˆJ˜ùwø·7í`æÙ¥ ßEâ}OK(}ˆ™§3¿¡Â»~j:ƒbÌs¤”£ág0ï‘ßãûDiÔ HN×%^°–™Óט1ß+2„€/LÓRÇç\ ŽˆÎ,‰¿';½ÊÌר™RPÔ5ÒËóÂ{ÑSj·À ©†almÅ^ùið/t:fóάV-ü0ìð¥ÁgLRmßfÓ4ÏÀ¡[ˆ4Ø4M+p€­Y.çI­ùfÓãKN'¢w`þ»õ éŸ M×còªóŒBwø]J9bvY»u ìttfæy†x‹™7ÑýðGŽûÉëõŽõù|‡ŒB¦ëzgfÞ¥:/ׄ2E_ò% `eJAÑàäMÝ àQ%yüøÞ½{‹‹ÕZʱÃáÅÌ¿»áé1›~FÂÖÙjÎyI‰˜D4Ä0Œ•-ù~¬Uî­>"ºþyá“2œ®ù­õF§%”žKDùœDta¾áH ™€]£Aù†#M‘ @ !>Ëpº.mHš™%ñ»²\ÎK…g3³ˆRdîfæËêBæŠÄOù³gN´%¤ž<Ñ1*_ZÒ³³yç3ý|-3¿ ½S§NW꺞wD 'ò±º®ï`æ%Dô›”òÌ”‚¢;,2oÔÎaW·©ÃOZ:™[„ÞÊaF>3? ïL/™ÖÚîqZBiWoø•ˆú­vëŸ6ÆuVUh_Ùl¶~D´À«éñ% ÞN5ë@Ü×Dôø×#€ˆ&y<žÂ’(½U(EºPp$!þÓÒžùgô_1¶°øfN"¢å¦z½Þb]×scbbŽkI÷¢ëzª®ë?3ó"¥Ì|õ˜Í;Ï·e×z«k\¨½ü:/3Ol ÷dgiåðx<kšÖÀp"š•át=‘år†­ðªh§ÑÑRÊc„’™&¢Ý·Hبs#VÊ׉ˆ‰èÒÕn½¨1m¹²Ü¾{H\å‘^¯w½†ÎÈîW÷±Ä4ÍgBLâdÕøþ…|u¾]2@žºîž–Z¶Ç*O«33O0NJy§®ëk‰èy·ÛýL$æ[×õˈè&f¾@.€„7ŒÞ´#ÀV«ÕøXÔ³Ë"zGæy<žV±ÚCoÐ4M{À%оa滳\ÎëABƒ¤”gQýá_ù^*˜y)€ÙáVƒËpºRUƒxy¾áh²}£Æ`fþ‚™ÿ™]šðP=Éü\f^¡¼óm¦iv¯ÇC] à‹²\α!Ø­;”:3ß8¶°øÅÖPÈ÷êÚ…™ïp€£”xˆ^p»Ý«š™Ä»1óH"º À ÜjÍÇ‚1›w~Þª<_éÛµ¸÷‰›ˆè‡1›wÞ‰yÌKJüÀ‰Jl6ÛÉ­Bß"ô¶Eê/ŽÒµÀ+RÊOf—µ+¨ú‡©qúÚl¶$fî§ÈûTE@Uá°@`>¶€®‚Î1LÎr9Ã6—Ÿátí°;ßp4¹ôe²î~@¿,—3ä¹Ûi ¥lbæ\úÔ·–z@;iY.gN£*gK)¿T‡§-om…}aÎ}„ã\ÿ f7ü; >eæO=ÏÆÆ¼¾Ãáèàç3ó ø·Oz|ÄÌÏ-,~­µ64‹{uÃÌ‹•ópþ˜Í;?¤ü-êÙ% Æ©¦iÎk-¶·†ÜÛLÓ4¯Ö4m"€™Šl{è%„ÈÊpº˜ˆ(¯Í V±¹Ú;˜yâKŸÏWPYYùoUαEGGŸADw*A Àüôø’™Ù¥ 3z3éñ%§Ñ¿‰(~Uº¦õB˜Ÿ'¢Ë¦·+0ë@Üšÿ»Duz@Di†a„Læ±±±Gx½Þ=*oCù¯”²cÐç}­±°Û²kcÁÜ$¥<—ˆÎ%¢ó‰º®ÿ`üm~$¢‰èÇŠŠŠŸC¬T«âãã3ˆèD!D7Á¥"å0¹à—ñýÀ{)E;à:jµR$¢ÀçÑFæ=ˆh£ª‡_y<ž'[“í-Bo[`Ó4±²²r¢”òv" 4îÄÌíkÎ!Úàf^ID» ÃØ…ƒÑÝþæµWVV®°Þáp<-¥| À1D43=¾äš0¨°®Œ¯š¥âDE}åóù ¥ìÊÿ2œ®y&¨Ãù†aWJ9˜ˆNW^´ÎÌ`fèºnÀ?T_¢ôJ "fæ8fN ¢åõ'$„X§BÂÂöf~Åf³}1zÓŽ¯ W¯¹m¡‘!¢3‚>_ay[¿®D%€[áWÓ´ÝBËEiié^÷xHӴ󉨟”²‹ZÐV àg)å±Dt=‚¦eáOW/hšÆ~P à7f.#"OPå‰cæŽRʨðÏv5 ³DGG7 !­,·ïNÖÝ8T'¨ ™ß¿Ø|bšæ !襼 _B]tœç›¿üö¾¶TöÇlÞù7ſŽº&*a¥îðÏo;ˆÈÉÌñêse7“ˆ*üÊÌn"ªPÛþ¶Ø®éuQ¯am¸‰91èsÜâ^]o³yçóÍ©¼¤Ä)1Îs<Ï·­Íð¡·mxLÓüþ*Õa‚¦i}™¹/õÐþÅFòª:>J‘lU⨉Œã2œ®‹²\Î@h."Beee{ ºÉ 7$®òH¿¦)ê@æÃ¼¥¿7M³A‘²„Iʾ[¢íg;ª­W‚1›wYMAxðlïŽðs•òÖìü‹zv9ˆÁ—¶˜¦ywk´¿EèjÃÓ4WÀ/µ ˆ=\J™èóùއ_ïü8ÇÑÌÿ¾Î?; þ°À‰èg)ådå]?'}¼«[Ú¶(bë׆ñz½ýÔõ)è’át ƒ?|4€ÝBˆ àDWo0s/Õ9*Dˆs²BMuÖÂX á­„ø›¼É¹Ïö>á˜Ñ›v4§æÁBø×ñx¥”)ªm²ÝBÛFyyùü@½æ®ívûvK1½}éì,—sj}ÒÉ.Mø.Ãé*fæë›ÃDt#3ÿ>ë@\­릷+;ÀrøçíL"év»ÃѰ%€”²>šúZP‡K³Jµ…pÁf³ÅÌUªN¬ÍçóÝØ\yÊKJÌ&¢tUo®¬¬ÜØZío)ÅYhRx<žWâ{ßQ´Æ7´É=à‚dÝ}ASÞÃ9öòþÌ|-¬í¼i ¥çI)?†ý@%3_kÆê†^_×õºX±¢wE1¬i!Ì]_•²v@}ß,«ÝõìrüQàsÃ0lÍö·ÝBÓ:!:ë1O]\vv}ÓÉr9³|à¥A1æñM‘÷§føº !Þðó£ûâjH/ÁÌËÄÁ/-y½Çãy'y ·3óY.çõht;¨ÿû¬Òh!œðz½ÁÛW£‰(À1ÝŸí}BrSæeq¯®‰D”ÿ´Ò^!ÄÍhe«Ú-B·Ðìp»Ýë¬Uä2²É] ZJùac“ú¸Ê#^y¼òE¥”WÔ4ÿŸát¥*iX€‡™Gz<ž×ÕfNª¯wž_2™¯S‡ñVi´Nô}[y•¯œõù|·6qv^…_x‰Œr»Ý»[ý‰U-44M»À|EP'g—&üPß´¦%”ž àMf6„W¬ªÐ¾w~h=ˆè=ŒÌr9߯Ìsàc øÅv®V; Âi»IDôPfIü}u´Ñù*dëIU:w-,~Ä*‘m‹zv¹Àp%ˆ#˜Ù€_Á𫱅Å/7À3Þ´5Ò‡ƒê’•BˆÄÑ›v4z„¿¼¤Ä,êðiÓ4oo ÏÔZg¡¹ð§Ž¼âì†$”YÿùôveÇ1ó»Rʯh/DGGg|QÝ`}æ‚|:á|÷ÍDT¿ªXï,—s{5„ÙYJù*ȼþ9þÆâ;Y‘ñ!=ô}»dJfÏò ˜ùOùM"ÚÈÌtB\iŶ¥yÿ6-Uå(øçsˆ(5/)Ñ­‚—<=fóÎtð™y=€ Õ;è§h)ejy°ÞÃ*Ó4ÓÚʳµõÈïñ=¢4ú›Ìlz|ÉD4Õ®†—™_µÙlw»ÝîŸr‡ã¬€:Þà‰ŽîÊÞVÓ¹N×%ðGU èÍÿNDÓ÷­_×¾ï¸\©ÊS Šb­Òùx¶÷ g3ó©RÊŽBˆ8øW{™y?í—RîcæŸÇmÙõÍ!o;€n!\ú#)åŒq[vm®#¡Nðx ?—FEEuµq{£CÉKJ¼~](¿DEEõU;sÚ,ÝB³™wQ'Ç„+ÍÌ’øiNW7)å=Dt‹”òÎdݽ›ˆÖ0ó—Bˆo|>ß¾¨¨¨Ÿì‹~åÂŽÞë*++ÛÙl¶vRÊ3˜ù," „XÀ ¢w¤”g—&üMUjFûò£|>ß""ª­LØ­ê×a¢ˆèf¦ëúPÃ0VÔ÷þ¤”gª?‚Ìs| :íÌÌϾ;kõñGw „MýM‰ËXd¡ÈKJ<À8WÿMq%€""Ú«†ÇÿlÇ™ù0":Œˆ:å%%À^fÞ.„XÁÌ/¦ý¨ÒLbV.B\—”˜•RP”q¨“…oI)çààP» òÔã+++n {)ö5ªÞ¹¥”—·%2·°8îjÁBÀçñ¬z®ßIíô—Rö-ù&oBŒ&¸oý‚]U¿k×gì"ZÀÑü©ÂúŽºž[[w)å#BˆË‚œÆ`UB!„x9œv{iðƒqPC~v[$sËC·ÐÜúç†ø,Ë噟¤È̼Ê*†CæÃœ£c†ûÖ/Ø ¿k8`0q¯®)5ð¬\`OPÇWTéßœ—”˜Ñ@›½Dæ_y<ž±m•ÌkÈÝBó"0ÿÜ•ˆ2™÷Y øcJ `+m°mZaì‰:‹¼ÆÌLz|IO"à|õZV%‚ܾƒu8슊Š:û÷¯žÚÂe²Ô¨Äᚦ]hšæÇuý#õW[Œ¶85(ß#Ô#QÖ¾õ æ"ëÁYˆèe3­¢¸@Þ¾õ sQÃwz…)-bæ‹zv¹jlañ›UŒ?úØß‘Dt$ü{ºOÐ~U©ªØJD¯I)_ñx<{4MÛÿœs3Ÿs`ã¢ÂPï»}ßq¿ÂröÓ4o Á^Ÿ8Ÿ™—d—&Œ òΗ¸„™ÿ8°qQÇÚÒ(¯0–;8í2%üa ·G–‡^ND—î[¿à‹ÆºF»>cŸm]õ2)erMÛÚõì2Rukªß à®”‚¢ÇC°Õ“ª»„Ú‚œEèZ œº®÷dæžz2óéDtjÞÛÝXðø’™—øplañ·AÈŸ*pÌ<òÀÆE¯Ô³Q]BD£l3M³{„^ ¿\ëmY.çÓA„¾ @wßïß°ð”Ct&Þp•:žRPô¾UôšÏö>á8)åÏû7,lÔë´ë3öJ"z«’.J)(J¬…€'x·²U×aeÌæ‡”‚^Ü«ëÌ|¿:ÜË̃<Ï÷V)²ÝB„ÃápÍÌ'3ó)ðÏûKDG2óѯ­8¼ðoQÛ¡^ß !ÖÞ´£Ú°° {tî+„Xÿº“eû7,¼¤¾÷Ծ︀ð†×4Í:M{ÙíöÓˆè;Röš]Öns¡ÿIÒD4kßúÓkhÌ_"¢ÔyKÇlÞyUÂ"‹zv9•ˆ¾Û¿aa£·ÉíûŽûÀ?Â.3/[X<¦–{¡<õ˜j<ôÀ}ÏÌ#ÇÖÆýDô€:tI)ϯ¬¬Ü`• ‹Ð-´|D9ŽÃ½^ïáQQQ)eŒ”òh!Dž"Þ÷‰èm)e¢œˆ ?Þ´#$ŶŽº¾ÊÌ×*¾Çþ ·Ö7Óú?—™?WÄšhÆ!çLu]ÏÌ ¸²\Ngðoéñ%§Ñ—88ePJDH)ÿ ÀKD]˜ùj"ꨮ¹aÌæ}­â9ØøØ¬î…KlÛ»î™]6!:72¡?`R#$ÍDtî˜Í;kœ2PãWáŸb«ŽÐ ’ˆ2ÇlÞyo/6ÔbR"Úçóùί¬¬Ül•‹Ð-´Rèº~#ÆÃå IDAT3¿ *ý Pu¨«Céî_R^»dГ4f~öÀÆE šƒL8#¥·b“òjNöx<‡ Lc·ÛÑŸg¹œçVý==¾ät"z@Ò!’ZžRPt±UR"yI‰ej}ec^ç§Ý{S’F̘‹Æ™Î*L)(êY‡‰@y® […cGoÚ±&/)1þm£™×kÛš…Ö†áê}o8È^»dPMðÓa­€BxêÔó&:K½W»=.»4áÛ,—3‰™¯ð€ª«}÷0óU™G4¾bæF99þèNyÌüX#%ßsQÏ.×ê$549€ÚF§N’R®ÎKJÜT…̇Zdnº…Ö;3_¤ˆ7l!KóÎ6•|“·. éý)NSYY™X‡¿,RvcæZ÷»g—&¼‘år^–årêBˆ3¡öäÑ‚ê¶Yˆ(,pSS\èÀÆE3lkŒ´‰¨N–R ŠÞU é&ѾZ8ª·ú\AD—WVVn²ŠŠEèZ9G?Nåù~Ž4ûdæ^ª¡ ׊ð€–öÿ|>ß!µhš¶jzì¶cÝu½ˆ”r@àRÊ×­Ùè9jü2'wìëYMq=f¾ É æ%%Öy÷FJAÑÜQë~ø–™gÖBì#¥|8::º—UZ,B·ÐÊ!¥ìÔX}Ž4_|ÆÚ z’&æ"õ± Žçd@wVÝcˆ;ë|kðö; ‘‰>wMßày)evS\ïÀÆEß1óÌFê,Œå|›Ý>hlañÌ1›wv ¢‡áß…RmgA±Q×õ¥š¦u³JEèZ)ž43ÿq¨hP! ÐhÈ}ë4ØëïØÿÖã¬|^Çû:3ÔNÊŒöåG:´ïØBã À™íúŒ½¾).v`ã¢YðˇAÐúÖãÀvÎê7 µãäßv»ý‡ÃÑß*6¡[he ¢žê½ ŒivRÿS§ãVÕX±Íf««0M•—:ºÏç¨ß>ŸÏš;o!H)(Ú —ˆr:ôß¹)®¹ÃÂTøçïÉžõùS^Rb>€aêðßÌ|*ü[Õª[<ED7J)¿Öu}½®ëc´·Ý‚…– ~WÀ/.ØÕ{y¼óc˜y¢:\év»¹Þn·Ÿ µ¿\JÊ4Â¥êý§q[v}cEêwØ‹*¡x™Ô/°"\éQ»PÎßóͺ¡j5{²úª0::ú\dzÕ4Í9¦iž¨Êô›ÜÕt”û2óBMÓöhšöž®ëcTÝiSgº…ÖÁæšv<€hU¹w…+])¥K}Œ CZOˆS ÞÌ:UP! ¤*f—µ E+Cûm«t´<0óÕÌ|Rû¾ãšLšwÿ†…C, S½©sij%}º¹,eä\Í^}AYYÙÿ‚“4Ms™išW›¦y3$¢×‰h5ðáÌœGDßiš¶_Ó´Uv»ý9»Ý~¿Ýn¿já¬Eè,D(ˆè¨ Ã]aL:Ö?Ê+Œz‡víÐo| j¨hFöÈ3s?õ^çi„ §k”¼&½g•Ž–‡±…Å߸ÀÐö}Ç}Є¤>ŽˆEõsסÔÇ_ër^^Rbw¯×û5üÁ‘àÓ4T!óª(óxŸï"z­C¿ñ㛈ÔWíß°°3€Ù}¯z©â‰Cy!Ä ø,™_0 ãFøc%4Ó4?0 ãÓ4gæ‘ð+'šê:w›¦9Õ"t "‡yè®0§˜W¼¤}ßqÔõOíûŽ»Tñ1üšÙÀ¦iÖYÁNÓ´;u”ˆê4>-¡´+”ª€eV±h¹XÒ§Û‘yI‰ïÑýeæ×™yAû¾ãòÛõ{JSäaÿ†…S…ÝäÕ‘Ø+Œ½iÇ/5—”xb€NŠd§{<ž›T†9ûg©i𗛦y$ ñx<ÙPꉡ[°¹ˆ |ýÍö•áL8¥ h6ü¡VàÅýÆw­í|Ÿ”»Ú÷÷€÷á_¡î#¢±¦i†4wGDç¨ï,—³N+÷™9Ðá6›íc«X´L<Ûû„³¼^ïFUvN1Mó^Ó4G¿һ3tè7~VSäåuÏü¼Ô=kŸ\ À*ü}®Ú«†¹{§Õ8翨g—+U½ˆUÜTÇ“Ù}Ã0V¢fÁšV+Úš…V»Ýþݨ8ìé/îÕu(3 ¿¨Å>wظè•*ù‰Dt3€ ÌØ»Àͦi~ê55M[ `0€O²\Î: d8]¸È ‘Úr±¨g—éD4ƒ™ï4MóÅêÎq82óåÏÜ¿aáü¦ÎgÇþ·ÇÌÑÌ| €GR ŠâQ‡F1ób6•Ì<Úãñ¼d=q‹Ð-X¨J~o¸²±ò’3dyÐû¤”[‰Èà8T‰ñÌÌÏxÄãñl«ç=Uˆ!¢e–Äß{¨ó½&¯ºû¥gÿ}/#¥ (Ë*- yI‰¹†I)ÿÏãñÔ„$66öhŸÏ÷€Ñ¶Qj8Ô ëƒö}ÇUÑíc6ï\R™ßÄÌÏÂ/ªäaæ‘ÇR0 3¬!w ­1Mpwø‚»­¼è™{ˆh©”²¿ÇãU_2w8gî©®’¯3:¹(2g"²‚±´<2Àiååå¶C‘9”——ï6 c,3'ø‰™?íÐoü«ûßzL3dÿCf¾¹†ûš¤:·Q*‰èèèè­'nº 5!º±/@DÀ?\è%¢{,€ïëû2óõçvÃ0®«¬¬\ßkù|¾ê£|ä÷øô:Uf!.WùÜ4fóÎb«H´(2Ïp”aCl6[çPþkšæ·†a\àRfî%¥ü¡}ßqš2ÿÌü*€A,}»ªÜ×dÁ?ì°Œ™Ïóz½…š¦}l·Û{XOß"t þÖ¦4fâ‹zv9™¯Q‡‹ ÃxØ4Íñ¦i5Ms¸išã<ÏËÌœ¦ÎÃPõñßQ ªc£: ¤”–v{ËóÌ7 cxCÒ1 ãÃ0ºxÀÃíûŽ[ѱÿ­Ç6Å=ظèuÈëuBzPù'€¹ŠÌ÷J)‡ìß°pÄþ —RöQš _Ûíök¬R`º MFèðˆÊm6ÛÌ&¸Ÿê}m]NÎpº†ãàJKîµåù]’ Ã8?\i†1;**ª;3ÇJ)··ë3öꦸ"Ú àbXÜ«k&=¨~ú™•|“÷gL’oò¾Ù·~Áu2ˆè¥ÿoïÞã£(Ï=€ÿžÙdg6á’G¯Ië’ ^*ê¡Õj«ÕžZZ«¢$V«¶§UÛ/ÕÚÖK½ÔÛ©p±¶bkÕV¶¶^ð†@$ᢵJµ¢­ $dg6Ù÷9ì»°ÆA6˜Íþ¾ŸO>3{›Í>;;¿}ggÞ7‘k¨Ï}^yÉÁ"òu{ñ®ŽŽŽwúò…¸®{2϶º{;û ;]c;á~nþÄ1Gø‘ªþWº—m;¢9BU׈È…5—ôùPõy9i^yÉ5ªz¹½ú=Uý\k}í«==¦eùÜ;Dä§"²0‰ȵ‚NÔ×-ôä§ã8·ï…×2mÛÔq^ÚÕ7­7UN³pUÈ Æ˜{\AZ¿€¹®{ºª>`Q"òM· «œuMúsö3Æ|ß^Þ¬ªÇµÖ×®Üéú»lÎUÖcêÀ3¯èD°];öц*Ù:¿7¾½^Kò÷óÞ°yð.n»~B{£LuæääÜÇU¡ÿ«--ú>€­¾ï§õ ¢çyŸ‘Ôù¾? ¢Ñè¨êÕÃ*g×W¯©é¯·MDâ @›ˆÜÛZ_ÛÛ/+ç8ÚŽiN tÊf"²mŒd¿µåÚt-w^yÉñHœcU½µ¯_G~~þ(lyª·]·^d§œQÿÆ¿¸6dÄúú}UMki‘Hd*€‡Däß÷?4ïûÿ àvU½gø”ÙÇôÅk^0è*›‘8…®ÀyqcÖ÷æ±-Ë義ª?UÕŸ"ÑU21Ð)[c¶ú¢i•¿Lcë|ºÝ/Åb¯õõëèêê:Ûw;îrt·Ko>ÀÁö¼kBÿWWVü -A,J×2]×-UÕ?XF§÷tß÷/°ÌÓ—½³5X‹Å~àSf÷8HKÁäê¯VÔüwêu­õµ×ˆyž÷]®% tÊîŸîá4.zª]æöÒKI Õ~ã–!÷âuÿÄξ0³¡i1ׄŒp–Ý-ž®–y±ˆü @³ïûÓv±¾œ`DaEMm_¼0Uý€$úi¿À…;ø?ÚÜ4¬rV÷Îh®ð-$úz':e©hÊF%Ì ¡P虾~yyyû!ÑëTõÁ]Ýÿò¡m³Œ³÷ÿ9WŒQ©ª/¤i9PUؼaÆÇvù!‰F›ü@õ°ÊYSûà‹õFà ‚{Œ>eöÑÝïײ|îã.WÕ»S;jY>÷vU <Ïû:W:eo ½=%ÐÝt,s^yÉg’Ÿ‘h4úb_¿†®®®*$ަG(Úi5ÁV]¨ª×Ú×ûRõÊup-èÿæOsϳ!Ë3Æ<‚Ä)ާ >üªÞ<Æ÷ý;Ø5~K´Ð7cûoà­ªú 1æìžîÛ²|îϼ¬ªww[ÆÝªz!×:e)cLGÊ!]»Ü°_6í¥/%5vöï×·ÚéùçWŽj+0@œ¿Ìúîi§{¼Éó¼cNñ}ÿÝ\×.VÕIÃ*g“Ö@qœ\¤œBê8Nˆ|u'_¾ àÄŠš“S®~À×uÇrua S–·ÐsrrÒÒBWÕAöËB_÷BÏóf%¿@¸mg÷½tðæ³${ÖZP³j}×€ÌpþеoðC¡Ðá{²×u—oŒ9)‹-ßÝÇG£ÑçDää^ž4¶ÐólMÙð¼ª~0¬rÖ¬žîßZ_»ÒOðƒ”ëVX‡”þˆNÙè[S6*^š–µÓÁ}ýÿ«êÕvöŸ7nò¿;ºßåCÛþSDØ‹«jl®â»Ÿq–¨êÙçyyyzž×("‡;Žó¹X,¶tÖïËì[XQ“Î=<x7uÕðKU½hGˆÇã7˜:|ÊìƒRþ·—”rUa S2Ƥº›¦Å6ÙiØgÚW­óŸØÏ^¼~G÷»lÈ–1ªz?ÈÄŒ1çðÏH7˜æyÞçw3Ì4Æ,0Úqœ;::–ìÉ?ÑÑÑñ–ªÆ\•Æ×ö)ùÐéAÔŠÈ~“«Ïèé¶÷&cÌi)ŸáØ—« ØBOK }fCÓ3|»Ìo÷Åÿ‰D>Òïõª· ¹k-óDd™ÝÈu8³fÕúz¾ó™§ª±ù1$†Ü]àºîa½yŒëºWcžðžˆTvtt¼¼§ÿ‡ëºW‹È»Z &W_¹§Ë³#»ªªÝ mSÕŸ‰ÈÎÂ{@ꜰX:1иi\î£6Ч»®{J:ÿçüüüQƘ_!1ÆzÜS½ƒ0¯VÕ'Tu˜½ê;UÍໞѡ~ªª6‹È_óòò>½£ûyžw’çy«Eäiõ}B4]¿§ÏßÞÞ¾PD.‘›Dä¹lø”ÙûïÉ21ÓÀqœ‡ºßÁ/l)¬¨éñl U}À‘)Wð×:e¡x<¾-ÐÇñÒµ\c̵âöâ¢H$rlš½°««ëIEöò-?o/X–z‡ï´}ùжeªZ‹Ä©I¾ˆTW56ßÁw<óU¯\7ED^4Æ,ö<﮼¼¼##‘H‰ëº<Ï»Äó¼¿ø³ 7ÿãþîÞ“#FD|0³¡iî̆¦§Uõ÷v°˜=q¹ªÞF{:%¯À—EdjO¡®ªë¤\u´ì…è”…-ô޾h¡W¯\·@r—øcÌŸ\×½xO–‡u]·Àx{Õ37nriòöKo>ë²![ž3Ƽ¤ª•öêcŽÙÐTËw{à˜ÙÐt&€‹œaŒyYU›Dd€_."ó¸ªz®ïûO¦ã9mŸï5"ò­äu999¿pü°ÊYë¨÷Šš;xŽãìð÷ø ÖŠÈ©>]0¹ú¥ÂŠšm§¥]2ã”v(˜\}xaEÍ7±Xìq®!»¹d h ð<ï û-.Jw+v^yɵªšº±Z¥ª7Äb±ÝêÛuÝïø)€ˆm™¬pã8G¨j…ˆ™²k¶U~Ë̆¦ò]Øæ•—œ€D·©ˆ¼yþе/×–Ý+"¾ïšÆÏJ³ˆ,ÙÐôµäuµ¥E›Ed(€NU=§µ¾¶×ýÌ«œu­ª^àô íÅóx@UǸ+ ÝòÁË¿ÜP0¹z³ª~; Ý¢ªWAÀ=Q tÊFƒ‹ÅÞO6r«›oJ÷sÔ–)"w Ñ¡KÒ»ªúgÇqžŽÇã«:;;»?.77·" ¡ªçÕ˧kð€ˆÜ8³¡©‰ïpvª++^ài;°J:Zç¿UÕ£¿þì+󼂫ìsœŠÄAz@¢„Uýqk}íõ;[VÁäê#DäNåªZcdé-Çó¼sTõ‡Ht_ü–ý\…Dä&ß÷ÀwŸNYÜHw]7j[½WV¯\÷ã¾x5ñõuåcî‘ °½#˜TqÙ²íþ‰ÎizÓ+˜ð†ˆ4c­^¹î7|K©®¬øuU½?‚kÒÐ2¿ ÀÏDdÚ̆¦gSžã§‹H‹ïûûåææŽsg!Ç~ýË»/ܵ`ä1ÌBbd¿ "r¦ª–©êŽã\åûþúû¹®;FU'€ã8+ödY t¢ÂuÝN9"rÝ̆¦+úúùjK‹¾ à "r"€Ñ»¸{àiUýµˆì@E¤EU79ŽóÁù+Ö¾ÄwzôÇ4ù¾Ñ¶ÌÏUÕ…ªú£ê•ë¶}9X0iìèx<þ§‰ÝAòwõP8>CDÎP‰íç„ÿÀkwgQ4}›ïRÿ‘ÃÐÒ`H;–Ù©ê•ëL ÷CEd€ÙÖ!†1fS(zéükŸp²ý#ê­¿ŠÈ<æºîtùµˆÜ_ÕØü¡–~WW×9"’ª:'å¦x,{Àƒ| èDŸ„­†Àp¶7U¯\÷7–ŸÒíÜ—V—.<ꈃÃápy,Ûí>û#‘ȹ"òKf64ÕývÛ‡ˆ¼Á*VœNÔ_´Û–F!KAAn^þ ¹Ðþ–½[<Ï»ÀB/œ·ìuÓýöÚÒ¢ ö9ؾ(Ãñ…Äi’”á¸Ë’¶…>œ¥ êÓjK‹®‘zÏóžPÕÅŽã´¨j€Cü€¹V9ŽsÆù+Ö¾°£eÕ–•ŠÈJûy¹ŸÕe õ+ªºID€-tªW®»®®¬x,€o‹È%ª:ʆr €'Uõ¾ªÆæG¼°‹En§ïû²² t¢~ED6ÚY: ä–ú›=\]`ºýëäé“O€»Û þ†Nª…ngCjâëY¢~ùl§± t¢þ·2;ÎÉùù“ÆMcEˆ>Êöp˜cýO¬¨ß1Æ$w¹#óÔ5¢ž[矵³ßÁØåÄ@'úÄ[è›Ræù;:QϦÚé3,¨ß·ÐU•§®õìSö3ò$KÁ@'ê—rrr¶ºˆ°"D6¯¼d*ì(›®ë>ÇŠ0Љú¥ŽŽŽ÷ÄÙB'ꙪN´³ÚÚÚ6²" t¢þª ‰ñš!"£Y¢z…åÈj t¢þMDÞµ³#Y ¢|>³³¯± t¢þÞyÛNÙB'ú¨"ì«Y :Q÷ŽÝ`1Љ>ªâñø–‚NÔß[èÉ]îû²DÛÕ–•&ç;;;›Y:Qÿ^¡çm;ëú­-ײ"D "r°õ´°" t¢Li¡ã¾ã&>ÈŠm ôä^«÷X :Q&ú†” جѶÏF¾ÝÌj0Љú½X,¶€Ú‹³"DÛ}°ne5èD™ Ûw)²…N”ÜØ;NˆˆÏj0Љ2ÅZ»áb h{ }†Y :Q¦l¸šÀÃ:Ñv!ûE×c)èD™âM¶Ð‰>"j§ t:QÆHvš±KA”òÛ9N”B¡ÐÚäú][Z4!Œ1Éz„Õ` eJ §vk9Ž!D$è„a õ{íííïc{燰"D€ˆlI¶Ð ô¬(S$G“:”¥ Tõ­ä|±" t¢L± Ça ¨[ ³[d:Qƺªò7t"Õ+×½Æ@g eÞŠí8«íìÐ…“ÇbEˆI›Ê]î t¢ÌFWÃÒÒÕÕUÆŠªºÞÎÁj0Љ2ÅëìFlËA¨êjÛR/g5èD™d%7^DÛ‰ÈjìÃ"‘È~¬(S6^«ì,(!ù™€1¦”å` ecLòÀ¸"Vƒ…BÉþ ªãY:QFÈÉÉY’l¬Ï+/9ž¡lwÞ+onð>ˆÈdV„N”:::ÞJUU'²"D€z;=’¥` e’gìt*KA¨êËvvÿH$ÂfèD³ñZ "Â@'J|–%çãñ8[é t¢Ì …Û`V[Ză€(ëûÒê¯Âvº$" t:QfˆF£`;˜‘cYÊv¹yù34Û‹•¬(c¨ê36Ðg5ˆ>ô;úd.+Â@'ÊŒ•Üq’»Ýù;:QâËíR;ëF"ŽuÀ@'ÊÏÙéȺ²b§JÜð;Îòä¼1¦‚a eß÷×Hö÷EV„²Ýù+Ö.€ªòÀ8:QFyÂNOe)ˆ¶u "N”A+ºãüÑÎ~záäq£XÊvªšÜí~€BV„N”¢ÑèRÙÀ‰Çã'³"ĺ4$g=Ï㈄ t¢ŒÑ¥ªÙ–É—XÊvñxü•”Ö:w»3Љ2Êoíô³,e»šUëW`ûqìE‘N”9‚ xVU7pkK‹ÎdEˆð*ˆN”Q:EäA»«a9(Û©êkvö0aV„N”1Dd‘6☃XÊêpœ&;‡ÃŬ(cø¾¿ÀZ!U­fE(›cþžòe—Î@'Ê(ªª ìÌL–ƒ²üÃðfÊÅ"V„N”Y+½ãÜÀد®¬˜ç¤SÖªnlú]ÊŃY:QFñ}ÿþ’ܦ±"”­Ä   ƒÎ@'Êd·ÛéióÊK¸!£l¶ÉNG° t¢ŒÁãHŒÀ–«ª—±"”Å6Ú)ûsg e¬»ìt&l!: X :Q¦¶Òmlñâñø·XÊR›è t¢L×àPÕKêo½þ–„²ªnµ³ì)ŽN”¹|ß¿ÀfƒV.œs1+BY,` èD™l3€:;_=¯¼„kP¶æ@Œ¥` eö‡Àqnมl""ƒè t¢!nÀößÒÏœW^2•U¡,2ÄN·² t¢ŒÁõ"Ò@ÜÊŠPIv(ó–‚N4´¨êÏl+}Ò¼ò’*–„²Dráu,h ´Òï°Þ†ú mþÉP§MM|=ìùçªÊ@g  QUý¶ñÀ)ScIh [0ù³S.2ÐèDG,û€G@Dέ++þ«B•1fâ¶0pœ¥¬h@‘ï"qÀäh`¯ëÇØÙÚa…‰N4pø¾ß¬ª?¶ÇÖ•ߪÐ@¤ª§Ú鳬h@ŠÅb7xÞ^üN]YñI¬ $ó'Ž9@ž½ø0+Â@'¨Œª~€Ä¹éw³$4ÀZç§ØÙx,û3+Â@'È­ô×Tõj{±¤®¬˜¡N)Ðϳ³[t°" t¢ê7øƒ½xÁ¼ò’¬ eºÚÒ¢¯Øß^ÌϪ0Љ|C&77·ÀÛª¹£®¬˜ã¦S¦»Däêºî4–„N4൷·¿ï8ÎtH dñ «B™ª®¬øó"r4c~`)ôÉ@'ÊÑhôE—Û‹ãkK‹îcU(CÝf§Ï·Ö×>à!UŽÄðÁÄ@'ø‚ ¸@ˆÈÙµ¥EW³*”IjK‹~`l¢qnþrrrî‘¡ápø4V(³ K@´[B®ëþÀçíåKª›Á²P7â˜cŒ1ÏÈpSËò¹—&o+¬¨y @~zV, æIDATÿÉJ±…N”-âápx€µöòÍìïú;5ñõƘ{m˜7¥†¹u'€ÂáðV‹N”5ÚÚÚ6ŠÈIHùžàùÇËÊP5oâØUJŽãœÝýö–åsÿà ¹€Õb eß÷›àùƘ'ëÊŠOde¨¿©--ú5€Ó@U«6.½gÉŽî à,ƒY5:QV ‚ ÉsŠˆ´ˆxx^y Ïç¥~£®¬øùº½xkk}íÏÎh|äg亮{+—™xPÑÊÍÍ …þ¬ªÃvõôªÆföMŸt˜ß ¹ ýž–åsgïê1Ã*g-0Ö÷ýɬ [èDY§³³³Þs€Û–úcueÅü-’>É0<%Ìçö&Ì@Uç«ê$ÏófÙB'ÊZápøS"òlï#»¶ª±¹š•¡½¥¶´h¼ˆüÀøäU-ËçîÖ:XXQó>€kƒ ¸“e (+Åb±×Ed*€×ìUÕµ¥EKL;šÕ¡½Ð*ÿ®ˆ,·aÞ¥ª/8fw—£ªÿ—ì–èDYË÷ýuA à‘£âñøªyå%ç±:ÔG­ò#êÊŠŸp3À"rR,;@¤°¢¦n7ùWUåoè t"ÐÁ—\ŠÄ€.#TuA]YñSueÅcYJ—yå%7ˆH€©É0vg’ïûOhqç«Î)¬¨9µ§ÇVÔ\VXQ³&õ:©0@+Ì@'"@ƒ ø¹ã8ÇXm¯›`UmiÑ,퉺²â‹êÊŠßUÕË‘èý­À…AœFßJÞ/.ðßæ±ÎÅÝ—Ó²|î†VÔ\–rÝëLnn.¿|2Љ(uƒÁD»Qm‘ëêÊŠß­++¾¢Ý òoÔ•¿à£ìÕ;ŽshwÐî±××<æ‚Ww°Øøa·ë6:޳/+žYx”;Ñ^’——7Ús³ª2ùÙ{ODæ˜3³¡©™U¢îæOs€ª^`Œ™%"ÃSnjð?A<ыŸ®»BUØZ_{÷ +j¶ˆÈ÷6-›3Ç^~WU/ŒÅbó` ˆºéèèxÇ÷ý¯©êU½@€}Tõûªº¶®¬ø©yå%ç°Rµ¥EgÔ•?jŒY§ª?H óFUýJ“zæÐ*"3Dä¶áSfÐÃí‹Tõ‹É ªŸïBfÉa ˆö®X,¶À9žç]©ªß0À ÓTuZ]Yq€§ü€¿T56¯eÕ²¦5~¬1æ+¦‹HjëX<."·ú¾ÿÔÇY¶ïûχÃá;Uõ¡î·©êŽãl;^D†©êÛ|G2 w¹}òò=Ïû’ªžÄs¡n·¿#"‹UõU}®zåºÕ,ÙÀQWVüEŸp:¶wJ”ð+U½5‹½ž†§sÂáðÕ²|îg’WTUî8NCËò¹:©jŠã8OA0@Œïˆ>†¼¼¼Ññxü+Np,G0øC+²IUW ±ëµ@cõÊu¯²z™a^yÉTŸ1Æ|FD*ñÑ=¥]þ*"‹|ßÀæ4ÿ 9žçýZUÇ©êY­õµ¯ T5Ùqœú–ås1¬rÖOTõð Îà»Å@'¢ô‡O‘[Uu¨ˆìlXËY£ª+¬‘Õ3–þíG¡pø8–ñ“³pò¸‘]]]•"R©ªS…ÄÏ+Ýż`Q8þ][[Û}½í÷<ïjU½Àè/á{Ƙ/9Žó4€3wã÷yb Qo„ÃáñŽã<«ª÷xRD¦¨êU-ëvÔsOþi~•1f€UÕ+×­aUû¤å}°ª~ Àx©°~Ðî®^ð´ª>‹ÅhÝÛÿs$92ßf÷´È°$‚㾫 t"J3ÏóŽWÕ'|·eùÜ»’ן2ûcÌUà8N©ªN0;?à5àUù›1fã8ï«ê³UÍo²Ò½ îU=LUp¨ã8‡Û ¼‹‡¾`±ª>››ûÌÖ­[ÿÝ_r ‰|ÅsŒªNP•¦ßë‰ND=q]÷4¿UÕ³[ëk·³ûT59 •ªêx‡8 À~»xŠ(€¿©êk6ð_ðj6e?â˜Uu €1É©ý‹Ä¹»²ÀRyYU—†Ãá¥mmm¹ˆ’-õsUõ9yÓ²9ÏìÎcãÆ¬ßç¨ožm[—‡§ý®Fƒó¬°>9UÕuªºþœçNõ ¯Ê¤ª‰¯¯+óyÇqF­ª#m Šmh— 1ÈIo½`ýôŠ1f©má*×Xb ÑÎZêßp…1æøÍ¯Ô5ìéò‚Xçâ‘Ç\°À7Uõ~{ðÝXn½ ¶6ôÿ²Gàoê>u§%oÊÍÍÝtöKk@:Ø[8yܨÎÎ΂P(T`Œ)PÕ)PÕÇq TµÀÉе›aý¡àVÕ7Ed €5"òjNNΚööö÷¸Vˆ>n¨ß àk"r̦esÒÒelaEÍc‚ Ô†´D"‘Tu¬Ýíœ ùq¶5ë¦ñ%u‰HÛ[Õª9ØõoÔ{üÝÀMš4©j³ª6uvv6ØÂ5èD”öÏn8þ•ˆT¶5¼/€}ðÑŽrö–.Ù¢ª­HA¾Ùþ½­ªÿrçmUý·ª¾íºî¿ö©bD t"êQØuÝ?ŠHá¦es*Ó±Àa•³ÎSÕù"r‚ïû‹Ó°ÈP^^Þ>]]]CUuˆã8CSv‡UÕ8ŽSØÛV´1¦CD6‹H`Œi‘vñãñøéÈÉÉÙÜÑѱÀV®"Ä@'¢L õGTµ°µ¾öÈt,°°¢æEC‚ (G¢Ã"Êm(³Å‚ ˜î8ŽSXQódoT0¹úð‚ÉÕ?êñ[¾ÈŒ‡ÃÓY^":í=m¾ïÀ¾öÀ¶]‘˜ˆÌ.¬¨ùH÷ž›–ÍY,"¯ˆÈ5ÜF1ЉhïjÉÍÍ=À…5/níðîôÎË羉Äà/Ÿ*¬¨y¾‡»üÀÏ󦲴D™¿¡ ,C]×}ÀHÛùÌúÝyø”Ùc^‘Å›–ÍùZêm…5ªúÛX,v>ËJÄ:í]›ƒ 8À‹ªº´°¢æs;»óÆ¥÷¼eŒ9MUO+¬¨¹´ÛÍKDä ,)ˆ>A5"r€‡ +jnÞé7€Wêê\àªÿ8ê›#SnZ `Ä AƒöaI‰èDô ñ}¿@¹ªUXQ³bø”ÙGíè¾v·tuu]“¼NUÃÇ]V“ˆNDŸlSýX,6ÀýƘ§ +jV9«ÇÝTõJ3‚Xçb‘´F£Ñ ¬$Q?‰Dös]÷.×u}×u_÷<ïÚH$rtǪ{5ùçºîû‘HäbÏóîw]W=Ï»†•#Ê <Ê(ËäççŒÇã©êÙD¢‹Ô·E¤]UÇÈG¢ô[}ßÿ†U#b Q?–››;ÑqœJ‡ðD$'/ñ<ïVBDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD4€ý?;SˆÇ³jêIEND®B`‚golang-github-francoispqt-gojay-1.2.13/gojay/000077500000000000000000000000001365331665100211055ustar00rootroot00000000000000golang-github-francoispqt-gojay-1.2.13/gojay/README.md000066400000000000000000000031241365331665100223640ustar00rootroot00000000000000# Gojay code generator This package provides a command line tool to generate gojay's marshaling and unmarshaling interface implementation for custom struct type(s) ## Get started ```sh go install github.com/francoispqt/gojay/gojay ``` ## Generate code ### Basic command The basic command is straightforward and easy to use: ```sh cd $GOPATH/src/github.com/user/project gojay -s . -p true -t MyType -o output.go ``` If you just want to the output to stdout, omit the -o flag. ### Using flags - s Source file/dir path, can be a relative or absolute path - t Types to generate with all its dependencies (comma separated) - a Annotation tag used to read metadata (default: json) - o Output file (relative or absolute path) - p Pool to reuse object (using sync.Pool) Examples: - Generate `SomeType` type in `/tmp/myproj` go package, write to file `output.go`: ```sh gojay -s /tmp/myproj -t SomeType -o output.go ``` - Generate type `SomeType` in file `somegofile.go`, with custom tag `gojay`, write to stdout: ```sh gojay -s somegofile.go -a gojay -t SomeType ``` ## Generator tags You can add tags to your structs to control: - the JSON key - skip a struct field - the use of omitempty methods for marshaling - timeFormat (java style data format) - timeLayout (golang time layout) ### Example: ```go type A struct { Str string `json:"string"` StrOmitEmpty string `json:"stringOrEmpty,omitempty"` Skip string `json:"-"` StartTime time.Time `json:"startDate" timeFormat:"yyyy-MM-dd HH:mm:ss"` EndTime *time.Time `json:"endDate" timeLayout:"2006-01-02 15:04:05"` } ``` golang-github-francoispqt-gojay-1.2.13/gojay/codegen/000077500000000000000000000000001365331665100225115ustar00rootroot00000000000000golang-github-francoispqt-gojay-1.2.13/gojay/codegen/field.go000066400000000000000000000122251365331665100241250ustar00rootroot00000000000000package codegen import ( "fmt" "github.com/viant/toolbox" "strings" ) //Field represents a field. type Field struct { Key string Init string OmitEmpty string TimeLayout string NullType string Name string Accessor string Mutator string Receiver string //alias and type name Alias string //object alias name Var string //variable for this field Type string RawType string HelperType string ComponentType string RawComponentType string IsPointerComponent bool PointerModifier string //takes field pointer, "&" if field is not a pointer type DereferenceModifier string //take pointer value, i.e "*" if field has a pointer type ComponentPointerModifier string //takes item pointer if needed,i.e ComponentDereferenceModifier string //de reference value if needed, i.e ComponentInitModifier string //takes item pointer if type is not a pointer type ComponentInit string //initialises component type DecodingMethod string EncodingMethod string PoolName string //pool name associated with this field ResetDependency string Reset string IsAnonymous bool IsPointer bool IsSlice bool GojayMethod string } //NewField returns a new field func NewField(owner *Struct, field *toolbox.FieldInfo, fieldType *toolbox.TypeInfo) (*Field, error) { typeName := normalizeTypeName(field.TypeName) var result = &Field{ IsAnonymous: field.IsAnonymous, Name: field.Name, RawType: field.TypeName, IsPointer: field.IsPointer, Key: getJSONKey(owner.options, field), Receiver: owner.Alias + " *" + owner.TypeInfo.Name, Type: typeName, Mutator: owner.Alias + "." + field.Name, Accessor: owner.Alias + "." + field.Name, ComponentType: field.ComponentType, IsPointerComponent: field.IsPointerComponent, Var: firstLetterToLowercase(field.Name), Init: fmt.Sprintf("%v{}", typeName), TimeLayout: "time.RFC3339", IsSlice: field.IsSlice, PoolName: getPoolName(field.TypeName), Alias: owner.Alias, Reset: "nil", } var err error if field.IsPointer { result.DereferenceModifier = "*" result.Init = "&" + result.Init } else { result.PointerModifier = "&" } if field.IsSlice { result.HelperType = getSliceHelperTypeName(field.ComponentType, field.IsPointerComponent) result.PoolName = getPoolName(field.ComponentType) } else if fieldType != nil { result.HelperType = getSliceHelperTypeName(fieldType.Name, field.IsPointerComponent) } if options := getTagOptions(field.Tag, "timeLayout"); len(options) > 0 { result.TimeLayout = wrapperIfNeeded(options[0], `"`) } else if options := getTagOptions(field.Tag, "timeFormat"); len(options) > 0 { result.TimeLayout = wrapperIfNeeded(toolbox.DateFormatToLayout(options[0]), `"`) } if strings.Contains(field.Tag, "omitempty") { result.OmitEmpty = "OmitEmpty" } if strings.Contains(field.Tag, "nullempty") { result.OmitEmpty = "NullEmpty" } if owner.options.PoolObjects { if field.IsPointer && !strings.HasSuffix(field.TypeName, ".Time") && !strings.Contains(field.TypeName, "sql.Null") { poolName := getPoolName(field.TypeName) result.Init = fmt.Sprintf(`%v.Get().(*%v)`, poolName, field.TypeName) } } encodingMethod := field.ComponentType if encodingMethod == "" { encodingMethod = result.Type } result.DecodingMethod = firstLetterToUppercase(encodingMethod) result.EncodingMethod = firstLetterToUppercase(encodingMethod) switch typeName { case "int", "int8", "int16", "int32", "int64", "uint", "uint8", "uint16", "uint32", "uint64": result.Reset = "0" case "float32", "float64": result.Reset = "0.0" case "string": result.Reset = `""` case "bool": result.Reset = "false" default: if field.IsSlice && owner.Type(field.ComponentType) != nil { var itemPointer = "" if !field.IsPointerComponent { itemPointer = "&" } result.ResetDependency, err = expandFieldTemplate(poolSliceInstanceRelease, struct { PoolName string Accessor string PointerModifier string }{PoolName: result.PoolName, Accessor: result.Accessor, PointerModifier: itemPointer}) if err != nil { return nil, err } } else if field.IsPointer && fieldType != nil { result.ResetDependency, err = expandFieldTemplate(poolInstanceRelease, struct { PoolName string Accessor string }{PoolName: result.PoolName, Accessor: result.Accessor}) if err != nil { return nil, err } } } if field.IsSlice || field.IsPointer { result.Reset = "nil" } if result.IsPointerComponent { result.ComponentInit = "&" + result.ComponentType + "{}" result.RawComponentType = "*" + result.ComponentType result.ComponentDereferenceModifier = "*" result.ComponentInitModifier = "&" } else { result.ComponentInit = result.ComponentType + "{}" result.RawComponentType = result.ComponentType result.ComponentPointerModifier = "&" } return result, nil } golang-github-francoispqt-gojay-1.2.13/gojay/codegen/generator.go000066400000000000000000000133541365331665100250340ustar00rootroot00000000000000package codegen import ( "fmt" "go/format" "io/ioutil" "os" "path/filepath" "strings" "github.com/viant/toolbox" ) const gojayPackage = "github.com/francoispqt/gojay" // Generator holds the content to generate the gojay code type Generator struct { fileInfo *toolbox.FileSetInfo types map[string]string structTypes map[string]string sliceTypes map[string]string pooledObjects map[string]string poolInit map[string]string imports map[string]bool filedInit []string Pkg string Code string Init string Imports string options *Options } // Returns the type from the the fileInfo func (g *Generator) Type(typeName string) *toolbox.TypeInfo { return g.fileInfo.Type(typeName) } // addImport adds an import package to be printed on the generated code func (g *Generator) addImport(pkg string) { g.imports[`"`+pkg+`"`] = true } // we initiate the variables containing the code to be generated func (g *Generator) init() { g.filedInit = []string{} g.imports = map[string]bool{} g.pooledObjects = map[string]string{} g.structTypes = map[string]string{} g.sliceTypes = map[string]string{} g.poolInit = map[string]string{} g.addImport(gojayPackage) // if we want pools, add the sync package right away if g.options.PoolObjects { g.addImport("sync") } } // NewGenerator creates a new generator with the given options func NewGenerator(options *Options) *Generator { var g = &Generator{} // first we validate the flags if err := options.Validate(); err != nil { panic(err) } g.options = options // we initiate the values on the generator g.init() return g } // Generate generates the gojay implementation code func (g *Generator) Generate() error { // first we read the code from which we should find the types if err := g.readPackageCode(g.options.Source); err != nil { return err } // then we generate code for the types given for _, rootType := range g.options.Types { if err := g.generateStructCode(rootType); err != nil { return err } } // g.Imports = strings.Join(toolbox.MapKeysToStringSlice(g.imports), "\n") return g.writeCode() } func (g *Generator) writeCode() error { var generatedCode = []string{} for _, key := range sortedKeys(g.pooledObjects) { code := g.pooledObjects[key] generatedCode = append(generatedCode, code) } generatedCode = append(generatedCode, "") for _, key := range sortedKeys(g.sliceTypes) { code := g.sliceTypes[key] generatedCode = append(generatedCode, code) } generatedCode = append(generatedCode, "") for _, key := range sortedKeys(g.structTypes) { code := g.structTypes[key] generatedCode = append(generatedCode, code) } for _, key := range sortedKeys(g.poolInit) { code := g.poolInit[key] if g.Init != "" { g.Init += "\n" } g.Init += code } g.Code = strings.Join(generatedCode, "\n") expandedCode, err := expandBlockTemplate(fileCode, g) if err != nil { return err } code, err := format.Source([]byte(expandedCode)) if err != nil { return err } // code destination is empty, we just print to stdout if g.options.Dest == "" { fmt.Print(string(code)) return nil } return ioutil.WriteFile(g.options.Dest, code, 0644) } func (g *Generator) generatePrimitiveArray(field *Field) error { key := field.ComponentType + toolbox.AsString(field.IsPointerComponent) if _, ok := g.sliceTypes[key]; ok { return nil } code, err := expandBlockTemplate(baseTypeSlice, field) g.sliceTypes[key] = code return err } func (g *Generator) generateObjectArray(field *Field) error { if _, ok := g.sliceTypes[field.RawComponentType]; ok { return nil } if err := g.generateStructCode(field.ComponentType); err != nil { return err } code, err := expandBlockTemplate(structTypeSlice, field) if err != nil { return err } g.sliceTypes[field.RawComponentType] = code return err } func (g *Generator) generateTimeArray(field *Field) error { if _, ok := g.sliceTypes[field.RawComponentType]; ok { return nil } code, err := expandBlockTemplate(timeSlice, field) if err != nil { return err } g.sliceTypes[field.RawComponentType] = code return err } func (g *Generator) generateTypedArray(field *Field) error { if _, ok := g.sliceTypes[field.RawComponentType]; ok { return nil } code, err := expandBlockTemplate(typeSlice, field) if err != nil { return err } g.sliceTypes[field.RawComponentType] = code return err } func (g *Generator) generatePool(structType string) error { if !g.options.PoolObjects { return nil } var err error if g.pooledObjects[structType], err = expandBlockTemplate(poolVar, struct { PoolName string }{getPoolName(structType)}); err == nil { g.poolInit[structType], err = expandBlockTemplate(poolInit, struct { PoolName string Type string }{getPoolName(structType), structType}) } return err } func (g *Generator) generateStructCode(structType string) error { structType = normalizeTypeName(structType) typeInfo := g.Type(structType) if typeInfo == nil { return nil } if _, hasCode := g.structTypes[structType]; hasCode { return nil } g.generatePool(structType) aStruct := NewStruct(typeInfo, g) code, err := aStruct.Generate() if err != nil { return err } g.structTypes[structType] = code return nil } func (g *Generator) readPackageCode(pkgPath string) error { p, err := filepath.Abs(pkgPath) if err != nil { return err } var f os.FileInfo if f, err = os.Stat(p); err != nil { // path/to/whatever does not exist return err } if !f.IsDir() { g.Pkg = filepath.Dir(p) dir, _ := filepath.Split(p) g.fileInfo, err = toolbox.NewFileSetInfo(dir) } else { g.Pkg = filepath.Base(p) g.fileInfo, err = toolbox.NewFileSetInfo(p) } // if Pkg flag is set use it if g.options.Pkg != "" { g.Pkg = g.options.Pkg } return err } golang-github-francoispqt-gojay-1.2.13/gojay/codegen/generator_test.go000066400000000000000000000032611365331665100260670ustar00rootroot00000000000000package codegen import ( "github.com/stretchr/testify/assert" "github.com/viant/toolbox" "log" "path" "testing" ) func TestGenerator_Generate(t *testing.T) { parent := path.Join(toolbox.CallerDirectory(3), "test") var useCases = []struct { description string options *Options hasError bool }{ { description: "basic struct code generation", options: &Options{ Source: path.Join(parent, "basic_struct"), Types: []string{"Message"}, Dest: path.Join(parent, "basic_struct", "encoding.go"), }, }, { description: "struct with pool code generation", options: &Options{ Source: path.Join(parent, "pooled_struct"), Types: []string{"Message"}, Dest: path.Join(parent, "pooled_struct", "encoding.go"), PoolObjects: true, }, }, { description: "struct with embedded type code generation", options: &Options{ Source: path.Join(parent, "embedded_struct"), Types: []string{"Message"}, Dest: path.Join(parent, "embedded_struct", "encoding.go"), PoolObjects: false, }, }, { description: "struct with json annotation and time/foarmat|layouat generation", options: &Options{ Source: path.Join(parent, "annotated_struct"), Types: []string{"Message"}, Dest: path.Join(parent, "annotated_struct", "encoding.go"), PoolObjects: false, TagName: "json", }, }, } for _, useCase := range useCases { gen := NewGenerator(useCase.options) err := gen.Generate() if useCase.hasError { assert.NotNil(t, err, useCase.description) continue } if !assert.Nil(t, err, useCase.description) { log.Fatal(err) continue } } } golang-github-francoispqt-gojay-1.2.13/gojay/codegen/helper.go000066400000000000000000000042631365331665100243240ustar00rootroot00000000000000package codegen import ( "github.com/viant/toolbox" "reflect" "strings" "sort" ) func firstLetterToUppercase(text string) string { return strings.ToUpper(string(text[0:1])) + string(text[1:]) } func firstLetterToLowercase(text string) string { return strings.ToLower(string(text[0:1])) + string(text[1:]) } func extractReceiverAlias(structType string) string { var result = string(structType[0]) for i := len(structType) - 1; i > 0; i-- { aChar := string(structType[i]) lowerChar := strings.ToLower(aChar) if lowerChar != aChar { result = lowerChar break } } return strings.ToLower(result) } func getTagOptions(tag, key string) []string { if tag == "" { return nil } var structTag = reflect.StructTag(strings.Replace(tag, "`", "", len(tag))) options, ok := structTag.Lookup(key) if !ok { return nil } return strings.Split(options, ",") } func getSliceHelperTypeName(typeName string, isPointer bool) string { if typeName == "" { return "" } var pluralName = firstLetterToUppercase(typeName) + "s" if isPointer { pluralName += "Ptr" } return strings.Replace(pluralName, ".", "", -1) } func isSkipable(options *Options, field *toolbox.FieldInfo) bool { if options := getTagOptions(field.Tag, options.TagName); len(options) > 0 { for _, candidate := range options { if candidate == "-" { return true } } } return false } func wrapperIfNeeded(text, wrappingChar string) string { if strings.HasPrefix(text, wrappingChar) { return text } return wrappingChar + text + wrappingChar } func getPoolName(typeName string) string { typeName = strings.Replace(typeName, "*", "", 1) return strings.Replace(typeName+"Pool", ".", "", -1) } func getJSONKey(options *Options, field *toolbox.FieldInfo) string { var key = field.Name if field.Tag != "" { if options := getTagOptions(field.Tag, options.TagName); len(options) > 0 { key = options[0] } } return key } func normalizeTypeName(typeName string) string { return strings.Replace(typeName, "*", "", strings.Count(typeName, "*")) } func sortedKeys(m map[string]string) ([]string) { keys := make([]string, len(m)) i := 0 for k := range m { keys[i] = k i++ } sort.Strings(keys) return keys }golang-github-francoispqt-gojay-1.2.13/gojay/codegen/options.go000066400000000000000000000024501365331665100245340ustar00rootroot00000000000000package codegen import ( "flag" "strings" "github.com/go-errors/errors" "github.com/viant/toolbox" "github.com/viant/toolbox/url" ) type Options struct { Source string Dest string Types []string PoolObjects bool TagName string Pkg string } func (o *Options) Validate() error { if o.Source == "" { return errors.New("Source was empty") } if len(o.Types) == 0 { return errors.New("Types was empty") } return nil } const ( optionKeySource = "s" optionKeyDest = "o" optionKeyTypes = "t" optionKeyTagName = "a" optionKeyPoolObjects = "p" optionKeyPkg = "pkg" ) //NewOptionsWithFlagSet creates a new options for the supplide flagset func NewOptionsWithFlagSet(set *flag.FlagSet) *Options { toolbox.Dump(set) var result = &Options{} result.Dest = set.Lookup(optionKeyDest).Value.String() result.Source = set.Lookup(optionKeySource).Value.String() result.PoolObjects = toolbox.AsBoolean(set.Lookup(optionKeyPoolObjects).Value.String()) result.TagName = set.Lookup(optionKeyTagName).Value.String() result.Types = strings.Split(set.Lookup(optionKeyTypes).Value.String(), ",") result.Pkg = set.Lookup(optionKeyPkg).Value.String() if result.Source == "" { result.Source = url.NewResource(".").ParsedURL.Path } return result } golang-github-francoispqt-gojay-1.2.13/gojay/codegen/struct.go000066400000000000000000000227241365331665100243730ustar00rootroot00000000000000package codegen import ( "fmt" "strings" "github.com/viant/toolbox" ) type Struct struct { *toolbox.TypeInfo referenced *toolbox.TypeInfo *Generator Alias string Init string Body string } //Generate generates decoderCode + structRelease + encoderCode func (s *Struct) Generate() (string, error) { return s.generateEncoding(s.TypeInfo) } func (s *Struct) generateEncoding(structInfo *toolbox.TypeInfo) (string, error) { var initEmbedded, decodingCases, err = s.generateFieldDecoding(structInfo.Fields()) if err != nil { return "", err } encodingCases, err := s.generateFieldEncoding(structInfo.Fields()) if err != nil { return "", err } var resetCode = "" if s.options.PoolObjects { resetCode, err = s.generateReset(structInfo.Fields()) if err != nil { return "", err } } var data = struct { Receiver string Alias string InitEmbedded string EncodingCases string DecodingCases string Reset string FieldCount int }{ Receiver: s.Alias + " *" + s.Name, DecodingCases: strings.Join(decodingCases, "\n"), EncodingCases: strings.Join(encodingCases, "\n"), FieldCount: len(decodingCases), InitEmbedded: initEmbedded, Reset: resetCode, Alias: s.Alias, } return expandBlockTemplate(encodingStructType, data) } func (s *Struct) generateReset(fields []*toolbox.FieldInfo) (string, error) { fieldReset, err := s.generateFieldReset(fields) if err != nil { return "", nil } return expandBlockTemplate(resetStruct, struct { Reset string Receiver string }{ Reset: strings.Join(fieldReset, "\n"), Receiver: s.Alias + " *" + s.Name, }) } func (s *Struct) generateFieldReset(fields []*toolbox.FieldInfo) ([]string, error) { fieldReset := []string{} for i := range fields { var templateKey = -1 fieldTypeInfo := s.Type(normalizeTypeName(fields[i].TypeName)) field, err := NewField(s, fields[i], fieldTypeInfo) if err != nil { return nil, err } if field.IsPointer || field.IsSlice || (fieldTypeInfo != nil && fieldTypeInfo.IsSlice) { templateKey = resetFieldValue } else { switch field.Type { case "int", "int8", "int16", "int32", "int64", "uint", "uint8", "uint16", "uint32", "uint64", "float32", "float64", "string", "bool", "[]string", "[]bool", "[]int", "[]int8", "[]int16", "[]int32", "[]int64", "[]uint", "[]uint8", "[]uint16", "[]uint32", "[]uint64", "[]float32", "[]float64", "[]byte": templateKey = resetFieldValue } } if templateKey != -1 { code, err := expandFieldTemplate(templateKey, field) if err != nil { return nil, err } fieldReset = append(fieldReset, code) } } return fieldReset, nil } func (s *Struct) generateFieldDecoding(fields []*toolbox.FieldInfo) (string, []string, error) { fieldCases := []string{} var initCode = "" for i := range fields { if isSkipable(s.options, fields[i]) { continue } var templateKey = -1 fieldTypeInfo := s.Type(normalizeTypeName(fields[i].TypeName)) field, err := NewField(s, fields[i], fieldTypeInfo) if err != nil { return "", nil, err } if fieldTypeInfo != nil { if err = s.generateStructCode(fieldTypeInfo.Name); err != nil { return "", nil, err } } if field.IsAnonymous { if fieldTypeInfo != nil { if field.IsPointer { init, err := expandBlockTemplate(embeddedStructInit, field) if err != nil { return "", nil, err } initCode += init } init, embeddedCases, err := s.generateFieldDecoding(fieldTypeInfo.Fields()) if err != nil { return "", nil, err } initCode += init fieldCases = append(fieldCases, embeddedCases...) } continue } main: switch field.Type { case "string", "bool", "int", "int8", "int16", "int32", "int64", "uint", "uint8", "uint16", "uint32", "uint64", "float32", "float64": templateKey = decodeBaseType case "[]string", "[]bool", "[]int", "[]int8", "[]int16", "[]int32", "[]int64", "[]uint", "[]uint8", "[]uint16", "[]uint32", "[]uint64", "[]float32", "[]float64": templateKey = decodeBaseTypeSlice s.generatePrimitiveArray(field) case "[]byte": templateKey = decodeRawType default: if fieldTypeInfo != nil { if !(field.IsSlice || fieldTypeInfo.IsSlice) { templateKey = decodeStruct break main } switch fieldTypeInfo.ComponentType { case "byte": templateKey = decodeRawType break main case "string", "bool", "int", "int8", "int16", "int32", "int64", "uint", "uint8", "uint16", "uint32", "uint64", "float32", "float64": s.generatePrimitiveArray(field) templateKey = decodeBaseTypeSlice break main } if err = s.generateStructCode(field.ComponentType); err != nil { return "", nil, err } templateKey = decodeStructSlice if err = s.generateObjectArray(field); err != nil { return "", nil, err } break main } else if field.IsSlice { if f, _, ok := s.typedFieldDecode(field, field.ComponentType); ok { templateKey = decodeStructSlice if err = f(field); err != nil { return "", nil, err } } else { templateKey = decodeStructSlice if err = s.generateObjectArray(field); err != nil { return "", nil, err } } } else if _, k, ok := s.typedFieldDecode(field, field.Type); ok { templateKey = k } else { // templateKey = decodeUnknown return "", nil, fmt.Errorf("Unknown type %s for field %s", field.Type, field.Name) } } if templateKey != -1 { decodingCase, err := expandFieldTemplate(templateKey, field) if err != nil { return "", nil, err } fieldCases = append(fieldCases, decodingCase) } } return initCode, fieldCases, nil } func (s *Struct) generateEmbeddedFieldEncoding(field *Field, fieldTypeInfo *toolbox.TypeInfo) ([]string, error) { var result = []string{} if fieldTypeInfo != nil { embeddedCases, err := s.generateFieldEncoding(fieldTypeInfo.Fields()) if err != nil { return nil, err } if field.IsPointer { result = append(result, fmt.Sprintf(" if %v != nil {", field.Accessor)) for _, code := range embeddedCases { result = append(result, " "+code) } result = append(result, " }") } else { result = append(result, embeddedCases...) } } return result, nil } func (s *Struct) generateFieldEncoding(fields []*toolbox.FieldInfo) ([]string, error) { fieldCases := []string{} for i := range fields { if isSkipable(s.options, fields[i]) { continue } var templateKey = -1 fieldTypeInfo := s.Type(normalizeTypeName(fields[i].TypeName)) field, err := NewField(s, fields[i], fieldTypeInfo) if err != nil { return nil, err } if field.IsAnonymous { embedded, err := s.generateEmbeddedFieldEncoding(field, fieldTypeInfo) if err != nil { return nil, err } fieldCases = append(fieldCases, embedded...) continue } main: switch field.Type { case "string", "bool", "int", "int8", "int16", "int32", "int64", "uint", "uint8", "uint16", "uint32", "uint64", "float32", "float64": templateKey = encodeBaseType case "[]string", "[]bool", "[]int", "[]int8", "[]int16", "[]int32", "[]int64", "[]uint", "[]uint8", "[]uint16", "[]uint32", "[]uint64", "[]float32", "[]float64": templateKey = encodeBaseTypeSlice s.generatePrimitiveArray(field) case "[]byte": templateKey = encodeRawType default: if fieldTypeInfo != nil { if !(field.IsSlice || fieldTypeInfo.IsSlice) { templateKey = encodeStruct break main } switch fieldTypeInfo.ComponentType { case "byte": templateKey = encodeRawType break main case "string", "bool", "int", "int8", "int16", "int32", "int64", "uint", "uint8", "uint16", "uint32", "uint64", "float32", "float64": templateKey = decodeBaseTypeSlice break main } templateKey = encodeStructSlice break main } else if field.IsSlice { templateKey = encodeStructSlice } else if _, k, ok := s.typedFieldEncode(field, field.Type); ok { templateKey = k } else { // templateKey = decodeUnknown return nil, fmt.Errorf("Unknown type %s for field %s", field.Type, field.Name) } } if templateKey != -1 { decodingCase, err := expandFieldTemplate(templateKey, field) if err != nil { return nil, err } fieldCases = append(fieldCases, decodingCase) } } return fieldCases, nil } var sqlNullTypes = []string{ "Bool", "Float64", "Int64", "String", "Time", } func (s *Struct) typedFieldEncode(field *Field, typeName string) (func(*Field) error, int, bool) { if strings.Contains(typeName, "time.Time") { return s.generateTimeArray, encodeTime, true } else if strings.Contains(typeName, "sql.Null") { for _, nullType := range sqlNullTypes { if strings.Contains(typeName, nullType) { field.NullType = nullType field.GojayMethod = "SQLNull" + nullType } } return s.generateTypedArray, encodeSQLNull, true } return nil, 0, false } func (s *Struct) typedFieldDecode(field *Field, typeName string) (func(*Field) error, int, bool) { if strings.Contains(typeName, "time.Time") { s.addImport("time") return s.generateTimeArray, decodeTime, true } else if strings.Contains(typeName, "sql.Null") { for _, nullType := range sqlNullTypes { if strings.Contains(typeName, nullType) { field.NullType = nullType field.GojayMethod = "SQLNull" + nullType } } s.addImport("database/sql") return s.generateTypedArray, decodeSQLNull, true } return nil, 0, false } func NewStruct(info *toolbox.TypeInfo, generator *Generator) *Struct { return &Struct{ TypeInfo: info, Generator: generator, Alias: extractReceiverAlias(info.Name), } } golang-github-francoispqt-gojay-1.2.13/gojay/codegen/template.go000066400000000000000000000174121365331665100246600ustar00rootroot00000000000000package codegen import ( "bytes" "fmt" "text/template" ) const ( decodeBaseType = iota encodeBaseType decodeBaseTypeSlice encodeBaseTypeSlice decodeRawType encodeRawType decodeStruct encodeStruct decodeStructSlice encodeStructSlice decodeTime encodeTime decodeSQLNull encodeSQLNull decodeUnknown encodeUnknown resetFieldValue poolInstanceRelease poolSliceInstanceRelease ) var fieldTemplate = map[int]string{ decodeBaseType: ` case "{{.Key}}": {{if .IsPointer}} var value {{.Type}} err := dec.{{.DecodingMethod}}(&value) if err == nil { {{.Accessor}} = &value } return err {{else}} return dec.{{.DecodingMethod}}(&{{.Accessor}}){{end}} `, encodeBaseType: ` enc.{{.EncodingMethod}}Key{{.OmitEmpty}}("{{.Key}}", {{.DereferenceModifier}}{{.Accessor}})`, decodeBaseTypeSlice: ` case "{{.Key}}": var aSlice = {{.HelperType}}{} err := dec.Array(&aSlice) if err == nil && len(aSlice) > 0 { {{.Mutator}} = {{.RawType}}(aSlice) } return err `, encodeBaseTypeSlice: ` var {{.Var}}Slice = {{.HelperType}}({{.Accessor}}) enc.ArrayKey{{.OmitEmpty}}("{{.Key}}",{{.Var}}Slice)`, decodeRawType: ` case "{{.Key}}": var value = gojay.EmbeddedJSON{} err := dec.AddEmbeddedJSON(&value) if err == nil && len(value) > 0 { {{.Mutator}} = {{.Type}}(value) } return err `, encodeRawType: ` var {{.Var}}Slice = gojay.EmbeddedJSON({{.Accessor}}) enc.AddEmbeddedJSONKey{{.OmitEmpty}}("{{.Key}}", &{{.Var}}Slice)`, decodeStruct: ` case "{{.Key}}":{{if .IsPointer}} var value = {{.Init}} err := dec.Object(value) if err == nil { {{.Mutator}} = value } {{else}} err := dec.Object(&{{.Mutator}}) {{end}} return err `, encodeStruct: ` enc.ObjectKey{{.OmitEmpty}}("{{.Key}}", {{.PointerModifier}}{{.Accessor}})`, decodeStructSlice: ` case "{{.Key}}": var aSlice = {{.HelperType}}{} err := dec.Array(&aSlice) if err == nil && len(aSlice) > 0 { {{.Mutator}} = {{.RawType}}(aSlice) } return err `, encodeStructSlice: ` var {{.Var}}Slice = {{.HelperType}}({{.Accessor}}) enc.ArrayKey{{.OmitEmpty}}("{{.Key}}", {{.DereferenceModifier}}{{.Var}}Slice)`, decodeTime: ` case "{{.Key}}": var format = {{.TimeLayout}} var value = {{.Init}} err := dec.Time({{.PointerModifier}}value, format) if err == nil { {{.Mutator}} = value } return err `, encodeTime: `{{if .IsPointer}} if {{.Accessor}} != nil { enc.TimeKey("{{.Key}}", {{.PointerModifier}}{{.Accessor}}, {{.TimeLayout}}) }{{else}} enc.TimeKey("{{.Key}}", {{.PointerModifier}}{{.Accessor}}, {{.TimeLayout}}){{end}}`, decodeSQLNull: ` case "{{.Key}}": var value = {{.Init}} err := dec.SQLNull{{.NullType}}({{.PointerModifier}}value) if err == nil { {{.Mutator}} = value } return err `, encodeSQLNull: `{{if .IsPointer}} if {{.Accessor}} != nil { enc.SQLNull{{.NullType}}Key{{.OmitEmpty}}("{{.Key}}", {{.PointerModifier}}{{.Accessor}}) }{{else}} enc.SQLNull{{.NullType}}Key{{.OmitEmpty}}("{{.Key}}", {{.PointerModifier}}{{.Accessor}}){{end}}`, decodeUnknown: ` case "{{.Key}}": return dec.Any({{.PointerModifier}}{{.Accessor}}) `, encodeUnknown: `{{if .IsPointer}} if {{.Accessor}} != nil { enc.Any({{.Accessor}}) }{{else}}enc.Any({{.Accessor}}){{end}}`, resetFieldValue: `{{if .ResetDependency}}{{.ResetDependency}} {{end}} {{.Mutator}} = {{.Reset}}`, poolInstanceRelease: ` {{.PoolName}}.Put({{.Accessor}})`, poolSliceInstanceRelease: ` for i := range {{.Accessor}} { {{.Accessor}}[i].Reset() {{.PoolName}}.Put({{.PointerModifier}}{{.Accessor}}[i]) }`, } const ( fileCode = iota encodingStructType baseTypeSlice structTypeSlice resetStruct poolVar poolInit embeddedStructInit timeSlice typeSlice ) var blockTemplate = map[int]string{ fileCode: `// Code generated by Gojay. DO NOT EDIT. package {{.Pkg}} import ( {{.Imports}} ) {{if .Init}} func init() { {{.Init}} } {{end}} {{.Code}} `, encodingStructType: `// MarshalJSONObject implements MarshalerJSONObject func ({{.Receiver}}) MarshalJSONObject(enc *gojay.Encoder) { {{.EncodingCases}} } // IsNil checks if instance is nil func ({{.Receiver}}) IsNil() bool { return {{.Alias}} == nil } // UnmarshalJSONObject implements gojay's UnmarshalerJSONObject func ({{.Receiver}}) UnmarshalJSONObject(dec *gojay.Decoder, k string) error { {{.InitEmbedded}} switch k { {{.DecodingCases}} } return nil } // NKeys returns the number of keys to unmarshal func ({{.Receiver}}) NKeys() int { return {{.FieldCount}} } {{.Reset}} `, baseTypeSlice: ` type {{.HelperType}} {{.RawType}} // UnmarshalJSONArray decodes JSON array elements into slice func (a *{{.HelperType}}) UnmarshalJSONArray(dec *gojay.Decoder) error { var value {{.ComponentType}} if err := dec.{{.DecodingMethod}}(&value); err != nil { return err } *a = append(*a, {{.ComponentInitModifier}}value) return nil } // MarshalJSONArray encodes arrays into JSON func (a {{.HelperType}}) MarshalJSONArray(enc *gojay.Encoder) { for _, item := range a { enc.{{.EncodingMethod}}({{.ComponentDereferenceModifier}}item) } } // IsNil checks if array is nil func (a {{.HelperType}}) IsNil() bool { return len(a) == 0 } `, structTypeSlice: ` type {{.HelperType}} {{.RawType}} func (s *{{.HelperType}}) UnmarshalJSONArray(dec *gojay.Decoder) error { var value = {{.ComponentInit}} if err := dec.Object({{.ComponentPointerModifier}}value); err != nil { return err } *s = append(*s, value) return nil } func (s {{.HelperType}}) MarshalJSONArray(enc *gojay.Encoder) { for i := range s { enc.Object({{.ComponentPointerModifier}}s[i]) } } func (s {{.HelperType}}) IsNil() bool { return len(s) == 0 } `, typeSlice: ` type {{.HelperType}} {{.RawType}} func (s *{{.HelperType}}) UnmarshalJSONArray(dec *gojay.Decoder) error { var value = {{.ComponentInit}} if err := dec.{{.GojayMethod}}({{.ComponentPointerModifier}}value); err != nil { return err } *s = append(*s, value) return nil } func (s {{.HelperType}}) MarshalJSONArray(enc *gojay.Encoder) { for i := range s { enc.{{.GojayMethod}}({{.ComponentPointerModifier}}s[i]) } } func (s {{.HelperType}}) IsNil() bool { return len(s) == 0 } `, timeSlice: ` type {{.HelperType}} {{.RawType}} func (s *{{.HelperType}}) UnmarshalJSONArray(dec *gojay.Decoder) error { var value = {{.ComponentInit}} if err := dec.Time({{.ComponentPointerModifier}}value, {{.TimeLayout}}); err != nil { return err } *s = append(*s, value) return nil } func (s {{.HelperType}}) MarshalJSONArray(enc *gojay.Encoder) { for i := range s { enc.Time({{.ComponentPointerModifier}}s[i], {{.TimeLayout}}) } } func (s {{.HelperType}}) IsNil() bool { return len(s) == 0 } `, resetStruct: ` // Reset reset fields func ({{.Receiver}}) Reset() { {{.Reset}} } `, poolVar: `var {{.PoolName}} *sync.Pool`, poolInit: ` {{.PoolName}} = &sync.Pool { New: func()interface{} { return &{{.Type}}{} }, }`, embeddedStructInit: `if {{.Accessor}} == nil { {{.Accessor}} = {{.Init}} }`, } func expandTemplate(namespace string, dictionary map[int]string, key int, data interface{}) (string, error) { var id = fmt.Sprintf("%v_%v", namespace, key) textTemplate, ok := dictionary[key] if !ok { return "", fmt.Errorf("failed to lookup template for %v.%v", namespace, key) } temlate, err := template.New(id).Parse(textTemplate) if err != nil { return "", fmt.Errorf("fiailed to parse template %v %v, due to %v", namespace, key, err) } writer := new(bytes.Buffer) err = temlate.Execute(writer, data) return writer.String(), err } func expandFieldTemplate(key int, data interface{}) (string, error) { return expandTemplate("fieldTemplate", fieldTemplate, key, data) } func expandBlockTemplate(key int, data interface{}) (string, error) { return expandTemplate("blockTemplate", blockTemplate, key, data) } golang-github-francoispqt-gojay-1.2.13/gojay/codegen/template_test.go000066400000000000000000000006061365331665100257140ustar00rootroot00000000000000package codegen import ( "github.com/stretchr/testify/assert" "testing" ) func Test_ExpandTemplate(t *testing.T) { var dictionary = map[int]string{ 1: `type {{.TypeName}} {{.SourceTypeName}}`, } expaded, err := expandTemplate("test", dictionary, 1, struct { TypeName string SourceTypeName string }{"A", "B"}) assert.Nil(t, err) assert.Equal(t, "type A B", expaded) } golang-github-francoispqt-gojay-1.2.13/gojay/codegen/test/000077500000000000000000000000001365331665100234705ustar00rootroot00000000000000golang-github-francoispqt-gojay-1.2.13/gojay/codegen/test/annotated_struct/000077500000000000000000000000001365331665100270515ustar00rootroot00000000000000golang-github-francoispqt-gojay-1.2.13/gojay/codegen/test/annotated_struct/encoding.go000066400000000000000000000137641365331665100312010ustar00rootroot00000000000000// Code generated by Gojay. DO NOT EDIT. package annotated_struct import ( "database/sql" "github.com/francoispqt/gojay" "time" ) type Ints []int // UnmarshalJSONArray decodes JSON array elements into slice func (a *Ints) UnmarshalJSONArray(dec *gojay.Decoder) error { var value int if err := dec.Int(&value); err != nil { return err } *a = append(*a, value) return nil } // MarshalJSONArray encodes arrays into JSON func (a Ints) MarshalJSONArray(enc *gojay.Encoder) { for _, item := range a { enc.Int(item) } } // IsNil checks if array is nil func (a Ints) IsNil() bool { return len(a) == 0 } type Float32s []float32 // UnmarshalJSONArray decodes JSON array elements into slice func (a *Float32s) UnmarshalJSONArray(dec *gojay.Decoder) error { var value float32 if err := dec.Float32(&value); err != nil { return err } *a = append(*a, value) return nil } // MarshalJSONArray encodes arrays into JSON func (a Float32s) MarshalJSONArray(enc *gojay.Encoder) { for _, item := range a { enc.Float32(item) } } // IsNil checks if array is nil func (a Float32s) IsNil() bool { return len(a) == 0 } type SubMessagesPtr []*SubMessage func (s *SubMessagesPtr) UnmarshalJSONArray(dec *gojay.Decoder) error { var value = &SubMessage{} if err := dec.Object(value); err != nil { return err } *s = append(*s, value) return nil } func (s SubMessagesPtr) MarshalJSONArray(enc *gojay.Encoder) { for i := range s { enc.Object(s[i]) } } func (s SubMessagesPtr) IsNil() bool { return len(s) == 0 } type SubMessages []SubMessage func (s *SubMessages) UnmarshalJSONArray(dec *gojay.Decoder) error { var value = SubMessage{} if err := dec.Object(&value); err != nil { return err } *s = append(*s, value) return nil } func (s SubMessages) MarshalJSONArray(enc *gojay.Encoder) { for i := range s { enc.Object(&s[i]) } } func (s SubMessages) IsNil() bool { return len(s) == 0 } // MarshalJSONObject implements MarshalerJSONObject func (p *Payload) MarshalJSONObject(enc *gojay.Encoder) { } // IsNil checks if instance is nil func (p *Payload) IsNil() bool { return p == nil } // UnmarshalJSONObject implements gojay's UnmarshalerJSONObject func (p *Payload) UnmarshalJSONObject(dec *gojay.Decoder, k string) error { switch k { } return nil } // NKeys returns the number of keys to unmarshal func (p *Payload) NKeys() int { return 0 } // MarshalJSONObject implements MarshalerJSONObject func (m *Message) MarshalJSONObject(enc *gojay.Encoder) { enc.IntKey("id", m.Id) enc.StringKey("name", m.Name) enc.Float64Key("price", m.Price) var intsSlice = Ints(m.Ints) enc.ArrayKey("ints", intsSlice) var floatsSlice = Float32s(m.Floats) enc.ArrayKey("floats", floatsSlice) enc.ObjectKey("subMessageX", m.SubMessageX) var messagesXSlice = SubMessagesPtr(m.MessagesX) enc.ArrayKey("messagesX", messagesXSlice) enc.ObjectKey("SubMessageY", &m.SubMessageY) var messagesYSlice = SubMessages(m.MessagesY) enc.ArrayKey("MessagesY", messagesYSlice) enc.BoolKey("enabled", *m.IsTrue) var payloadSlice = gojay.EmbeddedJSON(m.Payload) enc.AddEmbeddedJSONKey("data", &payloadSlice) if m.SQLNullString != nil { enc.SQLNullStringKey("sqlNullString", m.SQLNullString) } } // IsNil checks if instance is nil func (m *Message) IsNil() bool { return m == nil } // UnmarshalJSONObject implements gojay's UnmarshalerJSONObject func (m *Message) UnmarshalJSONObject(dec *gojay.Decoder, k string) error { switch k { case "id": return dec.Int(&m.Id) case "name": return dec.String(&m.Name) case "price": return dec.Float64(&m.Price) case "ints": var aSlice = Ints{} err := dec.Array(&aSlice) if err == nil && len(aSlice) > 0 { m.Ints = []int(aSlice) } return err case "floats": var aSlice = Float32s{} err := dec.Array(&aSlice) if err == nil && len(aSlice) > 0 { m.Floats = []float32(aSlice) } return err case "subMessageX": var value = &SubMessage{} err := dec.Object(value) if err == nil { m.SubMessageX = value } return err case "messagesX": var aSlice = SubMessagesPtr{} err := dec.Array(&aSlice) if err == nil && len(aSlice) > 0 { m.MessagesX = []*SubMessage(aSlice) } return err case "SubMessageY": err := dec.Object(&m.SubMessageY) return err case "MessagesY": var aSlice = SubMessages{} err := dec.Array(&aSlice) if err == nil && len(aSlice) > 0 { m.MessagesY = []SubMessage(aSlice) } return err case "enabled": var value bool err := dec.Bool(&value) if err == nil { m.IsTrue = &value } return err case "data": var value = gojay.EmbeddedJSON{} err := dec.AddEmbeddedJSON(&value) if err == nil && len(value) > 0 { m.Payload = Payload(value) } return err case "sqlNullString": var value = &sql.NullString{} err := dec.SQLNullString(value) if err == nil { m.SQLNullString = value } return err } return nil } // NKeys returns the number of keys to unmarshal func (m *Message) NKeys() int { return 12 } // MarshalJSONObject implements MarshalerJSONObject func (m *SubMessage) MarshalJSONObject(enc *gojay.Encoder) { enc.IntKey("id", m.Id) enc.StringKey("description", m.Description) enc.TimeKey("startDate", &m.StartTime, "2006-01-02 15:04:05") if m.EndTime != nil { enc.TimeKey("endDate", m.EndTime, "2006-01-02 15:04:05") } } // IsNil checks if instance is nil func (m *SubMessage) IsNil() bool { return m == nil } // UnmarshalJSONObject implements gojay's UnmarshalerJSONObject func (m *SubMessage) UnmarshalJSONObject(dec *gojay.Decoder, k string) error { switch k { case "id": return dec.Int(&m.Id) case "description": return dec.String(&m.Description) case "startDate": var format = "2006-01-02 15:04:05" var value = time.Time{} err := dec.Time(&value, format) if err == nil { m.StartTime = value } return err case "endDate": var format = "2006-01-02 15:04:05" var value = &time.Time{} err := dec.Time(value, format) if err == nil { m.EndTime = value } return err } return nil } // NKeys returns the number of keys to unmarshal func (m *SubMessage) NKeys() int { return 4 } golang-github-francoispqt-gojay-1.2.13/gojay/codegen/test/annotated_struct/encoding_test.go000066400000000000000000000041001365331665100322200ustar00rootroot00000000000000package annotated_struct import ( "bytes" "database/sql" "log" "testing" "github.com/francoispqt/gojay" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) var isTrue = true var msg = &Message{ Id: 1022, Name: "name acc", Price: 13.3, Ints: []int{1, 2, 5}, Floats: []float32{2.3, 4.6, 7.4}, SubMessageX: &SubMessage{ Id: 102, Description: "abcd", }, MessagesX: []*SubMessage{ &SubMessage{ Id: 2102, Description: "abce", }, }, SubMessageY: SubMessage{ Id: 3102, Description: "abcf", }, MessagesY: []SubMessage{ SubMessage{ Id: 5102, Description: "abcg", }, SubMessage{ Id: 5106, Description: "abcgg", }, }, IsTrue: &isTrue, Payload: []byte(`"123"`), SQLNullString: &sql.NullString{ String: "test", Valid: true, }, } var jsonData = `{ "id": 1022, "name": "name acc", "price": 13.3, "ints": [ 1, 2, 5 ], "floats": [ 2.3, 4.6, 7.4 ], "subMessageX": { "id": 102, "description": "abcd", "startDate": "0001-01-01 00:00:00" }, "messagesX": [ { "id": 2102, "description": "abce", "startDate": "0001-01-01 00:00:00" } ], "SubMessageY": { "id": 3102, "description": "abcf", "startDate": "0001-01-01 00:00:00" }, "MessagesY": [ { "id": 5102, "description": "abcg", "startDate": "0001-01-01 00:00:00" }, { "id": 5106, "description": "abcgg", "startDate": "0001-01-01 00:00:00" } ], "enabled": true, "data": "123", "sqlNullString": "test" }` func TestMessage_Unmarshal(t *testing.T) { var err error var data = []byte(jsonData) message := &Message{} err = gojay.UnmarshalJSONObject(data, message) if !assert.Nil(t, err) { log.Fatal(err) } require.Equal( t, msg, message, ) } func TestMessage_Marshal(t *testing.T) { var err error var writer = new(bytes.Buffer) encoder := gojay.NewEncoder(writer) err = encoder.Encode(msg) assert.Nil(t, err) var JSON = writer.String() require.JSONEq(t, jsonData, JSON) } golang-github-francoispqt-gojay-1.2.13/gojay/codegen/test/annotated_struct/message.go000066400000000000000000000012131365331665100310210ustar00rootroot00000000000000package annotated_struct import "database/sql" type Payload []byte type Message struct { Id int `json:"id"` Name string `json:"name"` Price float64 `json:"price"` Ints []int `json:"ints"` Floats []float32 `json:"floats"` SubMessageX *SubMessage `json:"subMessageX"` MessagesX []*SubMessage `json:"messagesX"` SubMessageY SubMessage MessagesY []SubMessage IsTrue *bool `json:"enabled"` Payload Payload `json:"data"` Ignore string `json:"-"` SQLNullString *sql.NullString `json:"sqlNullString"` } golang-github-francoispqt-gojay-1.2.13/gojay/codegen/test/annotated_struct/sub_message.go000066400000000000000000000004531365331665100316770ustar00rootroot00000000000000package annotated_struct import "time" type SubMessage struct { Id int `json:"id"` Description string `json:"description"` StartTime time.Time `json:"startDate" timeFormat:"yyyy-MM-dd HH:mm:ss"` EndTime *time.Time `json:"endDate" timeLayout:"2006-01-02 15:04:05"` } golang-github-francoispqt-gojay-1.2.13/gojay/codegen/test/basic_struct/000077500000000000000000000000001365331665100261555ustar00rootroot00000000000000golang-github-francoispqt-gojay-1.2.13/gojay/codegen/test/basic_struct/encoding.go000066400000000000000000000127741365331665100303050ustar00rootroot00000000000000// Code generated by Gojay. DO NOT EDIT. package basic_struct import ( "database/sql" "github.com/francoispqt/gojay" "time" ) type SubMessagesPtr []*SubMessage func (s *SubMessagesPtr) UnmarshalJSONArray(dec *gojay.Decoder) error { var value = &SubMessage{} if err := dec.Object(value); err != nil { return err } *s = append(*s, value) return nil } func (s SubMessagesPtr) MarshalJSONArray(enc *gojay.Encoder) { for i := range s { enc.Object(s[i]) } } func (s SubMessagesPtr) IsNil() bool { return len(s) == 0 } type SubMessages []SubMessage func (s *SubMessages) UnmarshalJSONArray(dec *gojay.Decoder) error { var value = SubMessage{} if err := dec.Object(&value); err != nil { return err } *s = append(*s, value) return nil } func (s SubMessages) MarshalJSONArray(enc *gojay.Encoder) { for i := range s { enc.Object(&s[i]) } } func (s SubMessages) IsNil() bool { return len(s) == 0 } type Ints []int // UnmarshalJSONArray decodes JSON array elements into slice func (a *Ints) UnmarshalJSONArray(dec *gojay.Decoder) error { var value int if err := dec.Int(&value); err != nil { return err } *a = append(*a, value) return nil } // MarshalJSONArray encodes arrays into JSON func (a Ints) MarshalJSONArray(enc *gojay.Encoder) { for _, item := range a { enc.Int(item) } } // IsNil checks if array is nil func (a Ints) IsNil() bool { return len(a) == 0 } type Float32s []float32 // UnmarshalJSONArray decodes JSON array elements into slice func (a *Float32s) UnmarshalJSONArray(dec *gojay.Decoder) error { var value float32 if err := dec.Float32(&value); err != nil { return err } *a = append(*a, value) return nil } // MarshalJSONArray encodes arrays into JSON func (a Float32s) MarshalJSONArray(enc *gojay.Encoder) { for _, item := range a { enc.Float32(item) } } // IsNil checks if array is nil func (a Float32s) IsNil() bool { return len(a) == 0 } // MarshalJSONObject implements MarshalerJSONObject func (m *SubMessage) MarshalJSONObject(enc *gojay.Encoder) { enc.IntKey("Id", m.Id) enc.StringKey("Description", m.Description) enc.TimeKey("StartTime", &m.StartTime, time.RFC3339) if m.EndTime != nil { enc.TimeKey("EndTime", m.EndTime, time.RFC3339) } } // IsNil checks if instance is nil func (m *SubMessage) IsNil() bool { return m == nil } // UnmarshalJSONObject implements gojay's UnmarshalerJSONObject func (m *SubMessage) UnmarshalJSONObject(dec *gojay.Decoder, k string) error { switch k { case "Id": return dec.Int(&m.Id) case "Description": return dec.String(&m.Description) case "StartTime": var format = time.RFC3339 var value = time.Time{} err := dec.Time(&value, format) if err == nil { m.StartTime = value } return err case "EndTime": var format = time.RFC3339 var value = &time.Time{} err := dec.Time(value, format) if err == nil { m.EndTime = value } return err } return nil } // NKeys returns the number of keys to unmarshal func (m *SubMessage) NKeys() int { return 4 } // MarshalJSONObject implements MarshalerJSONObject func (m *Message) MarshalJSONObject(enc *gojay.Encoder) { enc.IntKey("Id", m.Id) enc.StringKey("Name", m.Name) enc.Float64Key("Price", m.Price) var intsSlice = Ints(m.Ints) enc.ArrayKey("Ints", intsSlice) var floatsSlice = Float32s(m.Floats) enc.ArrayKey("Floats", floatsSlice) enc.ObjectKey("SubMessageX", m.SubMessageX) var messagesXSlice = SubMessagesPtr(m.MessagesX) enc.ArrayKey("MessagesX", messagesXSlice) enc.ObjectKey("SubMessageY", &m.SubMessageY) var messagesYSlice = SubMessages(m.MessagesY) enc.ArrayKey("MessagesY", messagesYSlice) enc.BoolKey("IsTrue", *m.IsTrue) var payloadSlice = gojay.EmbeddedJSON(m.Payload) enc.AddEmbeddedJSONKey("Payload", &payloadSlice) if m.SQLNullString != nil { enc.SQLNullStringKey("SQLNullString", m.SQLNullString) } } // IsNil checks if instance is nil func (m *Message) IsNil() bool { return m == nil } // UnmarshalJSONObject implements gojay's UnmarshalerJSONObject func (m *Message) UnmarshalJSONObject(dec *gojay.Decoder, k string) error { switch k { case "Id": return dec.Int(&m.Id) case "Name": return dec.String(&m.Name) case "Price": return dec.Float64(&m.Price) case "Ints": var aSlice = Ints{} err := dec.Array(&aSlice) if err == nil && len(aSlice) > 0 { m.Ints = []int(aSlice) } return err case "Floats": var aSlice = Float32s{} err := dec.Array(&aSlice) if err == nil && len(aSlice) > 0 { m.Floats = []float32(aSlice) } return err case "SubMessageX": var value = &SubMessage{} err := dec.Object(value) if err == nil { m.SubMessageX = value } return err case "MessagesX": var aSlice = SubMessagesPtr{} err := dec.Array(&aSlice) if err == nil && len(aSlice) > 0 { m.MessagesX = []*SubMessage(aSlice) } return err case "SubMessageY": err := dec.Object(&m.SubMessageY) return err case "MessagesY": var aSlice = SubMessages{} err := dec.Array(&aSlice) if err == nil && len(aSlice) > 0 { m.MessagesY = []SubMessage(aSlice) } return err case "IsTrue": var value bool err := dec.Bool(&value) if err == nil { m.IsTrue = &value } return err case "Payload": var value = gojay.EmbeddedJSON{} err := dec.AddEmbeddedJSON(&value) if err == nil && len(value) > 0 { m.Payload = []byte(value) } return err case "SQLNullString": var value = &sql.NullString{} err := dec.SQLNullString(value) if err == nil { m.SQLNullString = value } return err } return nil } // NKeys returns the number of keys to unmarshal func (m *Message) NKeys() int { return 12 } golang-github-francoispqt-gojay-1.2.13/gojay/codegen/test/basic_struct/encoding_test.go000066400000000000000000000037541365331665100313420ustar00rootroot00000000000000package basic_struct import ( "bytes" "database/sql" "testing" "github.com/francoispqt/gojay" "github.com/stretchr/testify/require" ) var isTrue = true var msg = &Message{ Id: 1022, Name: "name acc", Price: 13.3, Ints: []int{1, 2, 5}, Floats: []float32{2.3, 4.6, 7.4}, SubMessageX: &SubMessage{ Id: 102, Description: "abcd", }, MessagesX: []*SubMessage{ &SubMessage{ Id: 2102, Description: "abce", }, }, SubMessageY: SubMessage{ Id: 3102, Description: "abcf", }, MessagesY: []SubMessage{ SubMessage{ Id: 5102, Description: "abcg", }, SubMessage{ Id: 5106, Description: "abcgg", }, }, IsTrue: &isTrue, Payload: []byte(`"123"`), SQLNullString: &sql.NullString{ String: "test", Valid: true, }, } var jsonData = `{ "Id": 1022, "Name": "name acc", "Price": 13.3, "Ints": [ 1, 2, 5 ], "Floats": [ 2.3, 4.6, 7.4 ], "SubMessageX": { "Id": 102, "Description": "abcd", "StartTime": "0001-01-01T00:00:00Z" }, "MessagesX": [ { "Id": 2102, "Description": "abce", "StartTime": "0001-01-01T00:00:00Z" } ], "SubMessageY": { "Id": 3102, "Description": "abcf", "StartTime": "0001-01-01T00:00:00Z" }, "MessagesY": [ { "Id": 5102, "Description": "abcg", "StartTime": "0001-01-01T00:00:00Z" }, { "Id": 5106, "Description": "abcgg", "StartTime": "0001-01-01T00:00:00Z" } ], "IsTrue": true, "Payload": "123", "SQLNullString": "test" }` func TestMessage_Unmarshal(t *testing.T) { var err error var data = []byte(jsonData) message := &Message{} err = gojay.UnmarshalJSONObject(data, message) require.Nil(t, err) require.Equal(t, msg, message) } func TestMessage_Marshal(t *testing.T) { var writer = new(bytes.Buffer) encoder := gojay.NewEncoder(writer) var err = encoder.Encode(msg) require.Nil(t, err) var JSON = writer.String() require.JSONEq(t, jsonData, JSON) } golang-github-francoispqt-gojay-1.2.13/gojay/codegen/test/basic_struct/message.go000066400000000000000000000005531365331665100301330ustar00rootroot00000000000000package basic_struct import "database/sql" type Message struct { Id int Name string Price float64 Ints []int Floats []float32 SubMessageX *SubMessage MessagesX []*SubMessage SubMessageY SubMessage MessagesY []SubMessage IsTrue *bool Payload []byte SQLNullString *sql.NullString } golang-github-francoispqt-gojay-1.2.13/gojay/codegen/test/basic_struct/sub_message.go000066400000000000000000000002241365331665100307770ustar00rootroot00000000000000package basic_struct import "time" type SubMessage struct { Id int Description string StartTime time.Time EndTime *time.Time } golang-github-francoispqt-gojay-1.2.13/gojay/codegen/test/embedded_struct/000077500000000000000000000000001365331665100266255ustar00rootroot00000000000000golang-github-francoispqt-gojay-1.2.13/gojay/codegen/test/embedded_struct/encoding.go000066400000000000000000000146211365331665100307460ustar00rootroot00000000000000// Code generated by Gojay. DO NOT EDIT. package embedded_struct import ( "github.com/francoispqt/gojay" "time" ) type Ints []int // UnmarshalJSONArray decodes JSON array elements into slice func (a *Ints) UnmarshalJSONArray(dec *gojay.Decoder) error { var value int if err := dec.Int(&value); err != nil { return err } *a = append(*a, value) return nil } // MarshalJSONArray encodes arrays into JSON func (a Ints) MarshalJSONArray(enc *gojay.Encoder) { for _, item := range a { enc.Int(item) } } // IsNil checks if array is nil func (a Ints) IsNil() bool { return len(a) == 0 } type Float64s []float64 // UnmarshalJSONArray decodes JSON array elements into slice func (a *Float64s) UnmarshalJSONArray(dec *gojay.Decoder) error { var value float64 if err := dec.Float64(&value); err != nil { return err } *a = append(*a, value) return nil } // MarshalJSONArray encodes arrays into JSON func (a Float64s) MarshalJSONArray(enc *gojay.Encoder) { for _, item := range a { enc.Float64(item) } } // IsNil checks if array is nil func (a Float64s) IsNil() bool { return len(a) == 0 } type SubMessagesPtr []*SubMessage func (s *SubMessagesPtr) UnmarshalJSONArray(dec *gojay.Decoder) error { var value = &SubMessage{} if err := dec.Object(value); err != nil { return err } *s = append(*s, value) return nil } func (s SubMessagesPtr) MarshalJSONArray(enc *gojay.Encoder) { for i := range s { enc.Object(s[i]) } } func (s SubMessagesPtr) IsNil() bool { return len(s) == 0 } type SubMessages []SubMessage func (s *SubMessages) UnmarshalJSONArray(dec *gojay.Decoder) error { var value = SubMessage{} if err := dec.Object(&value); err != nil { return err } *s = append(*s, value) return nil } func (s SubMessages) MarshalJSONArray(enc *gojay.Encoder) { for i := range s { enc.Object(&s[i]) } } func (s SubMessages) IsNil() bool { return len(s) == 0 } // MarshalJSONObject implements MarshalerJSONObject func (m *SubMessage) MarshalJSONObject(enc *gojay.Encoder) { enc.StringKey("Description", m.Description) enc.TimeKey("StartTime", &m.StartTime, time.RFC3339) if m.EndTime != nil { enc.TimeKey("EndTime", m.EndTime, time.RFC3339) } } // IsNil checks if instance is nil func (m *SubMessage) IsNil() bool { return m == nil } // UnmarshalJSONObject implements gojay's UnmarshalerJSONObject func (m *SubMessage) UnmarshalJSONObject(dec *gojay.Decoder, k string) error { switch k { case "Description": return dec.String(&m.Description) case "StartTime": var format = time.RFC3339 var value = time.Time{} err := dec.Time(&value, format) if err == nil { m.StartTime = value } return err case "EndTime": var format = time.RFC3339 var value = &time.Time{} err := dec.Time(value, format) if err == nil { m.EndTime = value } return err } return nil } // NKeys returns the number of keys to unmarshal func (m *SubMessage) NKeys() int { return 3 } // MarshalJSONObject implements MarshalerJSONObject func (m *Message) MarshalJSONObject(enc *gojay.Encoder) { if m.BaseId != nil { enc.IntKey("Id", m.Id) enc.StringKey("Name", m.Name) } enc.StringKey("Description", m.Description) enc.TimeKey("StartTime", &m.StartTime, time.RFC3339) if m.EndTime != nil { enc.TimeKey("EndTime", m.EndTime, time.RFC3339) } enc.Float64Key("Price", m.Price) var intsSlice = Ints(m.Ints) enc.ArrayKey("Ints", intsSlice) var floatsSlice = Float64s(m.Floats) enc.ArrayKey("Floats", floatsSlice) enc.ObjectKey("SubMessageX", m.SubMessageX) var messagesXSlice = SubMessagesPtr(m.MessagesX) enc.ArrayKey("MessagesX", messagesXSlice) enc.ObjectKey("SubMessageY", &m.SubMessageY) var messagesYSlice = SubMessages(m.MessagesY) enc.ArrayKey("MessagesY", messagesYSlice) enc.BoolKey("IsTrue", *m.IsTrue) var payloadSlice = gojay.EmbeddedJSON(m.Payload) enc.AddEmbeddedJSONKey("Payload", &payloadSlice) } // IsNil checks if instance is nil func (m *Message) IsNil() bool { return m == nil } // UnmarshalJSONObject implements gojay's UnmarshalerJSONObject func (m *Message) UnmarshalJSONObject(dec *gojay.Decoder, k string) error { if m.BaseId == nil { m.BaseId = &BaseId{} } switch k { case "Id": return dec.Int(&m.Id) case "Name": return dec.String(&m.Name) case "Description": return dec.String(&m.Description) case "StartTime": var format = time.RFC3339 var value = time.Time{} err := dec.Time(&value, format) if err == nil { m.StartTime = value } return err case "EndTime": var format = time.RFC3339 var value = &time.Time{} err := dec.Time(value, format) if err == nil { m.EndTime = value } return err case "Price": return dec.Float64(&m.Price) case "Ints": var aSlice = Ints{} err := dec.Array(&aSlice) if err == nil && len(aSlice) > 0 { m.Ints = []int(aSlice) } return err case "Floats": var aSlice = Float64s{} err := dec.Array(&aSlice) if err == nil && len(aSlice) > 0 { m.Floats = []float64(aSlice) } return err case "SubMessageX": var value = &SubMessage{} err := dec.Object(value) if err == nil { m.SubMessageX = value } return err case "MessagesX": var aSlice = SubMessagesPtr{} err := dec.Array(&aSlice) if err == nil && len(aSlice) > 0 { m.MessagesX = []*SubMessage(aSlice) } return err case "SubMessageY": err := dec.Object(&m.SubMessageY) return err case "MessagesY": var aSlice = SubMessages{} err := dec.Array(&aSlice) if err == nil && len(aSlice) > 0 { m.MessagesY = []SubMessage(aSlice) } return err case "IsTrue": var value bool err := dec.Bool(&value) if err == nil { m.IsTrue = &value } return err case "Payload": var value = gojay.EmbeddedJSON{} err := dec.AddEmbeddedJSON(&value) if err == nil && len(value) > 0 { m.Payload = []byte(value) } return err } return nil } // NKeys returns the number of keys to unmarshal func (m *Message) NKeys() int { return 14 } // MarshalJSONObject implements MarshalerJSONObject func (i *BaseId) MarshalJSONObject(enc *gojay.Encoder) { enc.IntKey("Id", i.Id) enc.StringKey("Name", i.Name) } // IsNil checks if instance is nil func (i *BaseId) IsNil() bool { return i == nil } // UnmarshalJSONObject implements gojay's UnmarshalerJSONObject func (i *BaseId) UnmarshalJSONObject(dec *gojay.Decoder, k string) error { switch k { case "Id": return dec.Int(&i.Id) case "Name": return dec.String(&i.Name) } return nil } // NKeys returns the number of keys to unmarshal func (i *BaseId) NKeys() int { return 2 } golang-github-francoispqt-gojay-1.2.13/gojay/codegen/test/embedded_struct/encoding_test.go000066400000000000000000000031451365331665100320040ustar00rootroot00000000000000package embedded_struct import ( "bytes" "github.com/francoispqt/gojay" "github.com/stretchr/testify/assert" "github.com/viant/assertly" "testing" ) func TestMessage_Unmarshal(t *testing.T) { input := `{ "Id": 1022, "Name": "name acc", "Description": "abcd", "Price": 13.3, "Ints": [ 1, 2, 5 ], "Floats": [ 2.3, 4.6, 7.4 ], "MessagesX": [ { "Description": "abce" } ], "SubMessageY": { "Description": "abcf" }, "MessagesY": [ { "Description": "abcg" }, { "Description": "abcgg" } ], "IsTrue": true, "Payload": "" }` var err error var data = []byte(input) message := &Message{} err = gojay.UnmarshalJSONObject(data, message) assert.Nil(t, err) assertly.AssertValues(t, input, message) } func TestMessage_Marshal(t *testing.T) { input := `{ "Id": 1022, "Name": "name acc", "Description": "abcd", "Price": 13.3, "Ints": [ 1, 2, 5 ], "Floats": [ 2.3, 4.6, 7.4 ], "MessagesX": [ { "Description": "abce" } ], "SubMessageY": { "Description": "abcf" }, "MessagesY": [ { "Description": "abcg" }, { "Description": "abcgg" } ], "IsTrue": true, "Payload": "" }` var err error var data = []byte(input) message := &Message{} err = gojay.UnmarshalJSONObject(data, message) assert.Nil(t, err) assertly.AssertValues(t, input, message) var writer = new(bytes.Buffer) encoder := gojay.NewEncoder(writer) err = encoder.Encode(message) assert.Nil(t, err) var JSON = writer.String() assertly.AssertValues(t, input, JSON) } golang-github-francoispqt-gojay-1.2.13/gojay/codegen/test/embedded_struct/message.go000066400000000000000000000005011365331665100305740ustar00rootroot00000000000000package embedded_struct type BaseId struct { Id int Name string } type Message struct { *BaseId SubMessage Price float64 Ints []int Floats []float64 SubMessageX *SubMessage MessagesX []*SubMessage SubMessageY SubMessage MessagesY []SubMessage IsTrue *bool Payload []byte } golang-github-francoispqt-gojay-1.2.13/gojay/codegen/test/embedded_struct/sub_message.go000066400000000000000000000002061365331665100314470ustar00rootroot00000000000000package embedded_struct import "time" type SubMessage struct { Description string StartTime time.Time EndTime *time.Time } golang-github-francoispqt-gojay-1.2.13/gojay/codegen/test/pooled_struct/000077500000000000000000000000001365331665100263565ustar00rootroot00000000000000golang-github-francoispqt-gojay-1.2.13/gojay/codegen/test/pooled_struct/encoding.go000066400000000000000000000140601365331665100304740ustar00rootroot00000000000000// Code generated by Gojay. DO NOT EDIT. package pooled_struct import ( "github.com/francoispqt/gojay" "sync" "time" ) func init() { MessagePool = &sync.Pool{ New: func() interface{} { return &Message{} }, } SubMessagePool = &sync.Pool{ New: func() interface{} { return &SubMessage{} }, } } var MessagePool *sync.Pool var SubMessagePool *sync.Pool type Ints []int // UnmarshalJSONArray decodes JSON array elements into slice func (a *Ints) UnmarshalJSONArray(dec *gojay.Decoder) error { var value int if err := dec.Int(&value); err != nil { return err } *a = append(*a, value) return nil } // MarshalJSONArray encodes arrays into JSON func (a Ints) MarshalJSONArray(enc *gojay.Encoder) { for _, item := range a { enc.Int(item) } } // IsNil checks if array is nil func (a Ints) IsNil() bool { return len(a) == 0 } type Float64s []float64 // UnmarshalJSONArray decodes JSON array elements into slice func (a *Float64s) UnmarshalJSONArray(dec *gojay.Decoder) error { var value float64 if err := dec.Float64(&value); err != nil { return err } *a = append(*a, value) return nil } // MarshalJSONArray encodes arrays into JSON func (a Float64s) MarshalJSONArray(enc *gojay.Encoder) { for _, item := range a { enc.Float64(item) } } // IsNil checks if array is nil func (a Float64s) IsNil() bool { return len(a) == 0 } type SubMessagesPtr []*SubMessage func (s *SubMessagesPtr) UnmarshalJSONArray(dec *gojay.Decoder) error { var value = &SubMessage{} if err := dec.Object(value); err != nil { return err } *s = append(*s, value) return nil } func (s SubMessagesPtr) MarshalJSONArray(enc *gojay.Encoder) { for i := range s { enc.Object(s[i]) } } func (s SubMessagesPtr) IsNil() bool { return len(s) == 0 } type SubMessages []SubMessage func (s *SubMessages) UnmarshalJSONArray(dec *gojay.Decoder) error { var value = SubMessage{} if err := dec.Object(&value); err != nil { return err } *s = append(*s, value) return nil } func (s SubMessages) MarshalJSONArray(enc *gojay.Encoder) { for i := range s { enc.Object(&s[i]) } } func (s SubMessages) IsNil() bool { return len(s) == 0 } // MarshalJSONObject implements MarshalerJSONObject func (m *SubMessage) MarshalJSONObject(enc *gojay.Encoder) { enc.IntKey("Id", m.Id) enc.StringKey("Description", m.Description) enc.TimeKey("StartTime", &m.StartTime, time.RFC3339) if m.EndTime != nil { enc.TimeKey("EndTime", m.EndTime, time.RFC3339) } } // IsNil checks if instance is nil func (m *SubMessage) IsNil() bool { return m == nil } // UnmarshalJSONObject implements gojay's UnmarshalerJSONObject func (m *SubMessage) UnmarshalJSONObject(dec *gojay.Decoder, k string) error { switch k { case "Id": return dec.Int(&m.Id) case "Description": return dec.String(&m.Description) case "StartTime": var format = time.RFC3339 var value = time.Time{} err := dec.Time(&value, format) if err == nil { m.StartTime = value } return err case "EndTime": var format = time.RFC3339 var value = &time.Time{} err := dec.Time(value, format) if err == nil { m.EndTime = value } return err } return nil } // NKeys returns the number of keys to unmarshal func (m *SubMessage) NKeys() int { return 4 } // Reset reset fields func (m *SubMessage) Reset() { m.Id = 0 m.Description = "" m.EndTime = nil } // MarshalJSONObject implements MarshalerJSONObject func (m *Message) MarshalJSONObject(enc *gojay.Encoder) { enc.IntKey("Id", m.Id) enc.StringKey("Name", m.Name) enc.Float64Key("Price", m.Price) var intsSlice = Ints(m.Ints) enc.ArrayKey("Ints", intsSlice) var floatsSlice = Float64s(m.Floats) enc.ArrayKey("Floats", floatsSlice) enc.ObjectKey("SubMessageX", m.SubMessageX) var messagesXSlice = SubMessagesPtr(m.MessagesX) enc.ArrayKey("MessagesX", messagesXSlice) enc.ObjectKey("SubMessageY", &m.SubMessageY) var messagesYSlice = SubMessages(m.MessagesY) enc.ArrayKey("MessagesY", messagesYSlice) enc.BoolKey("IsTrue", *m.IsTrue) var payloadSlice = gojay.EmbeddedJSON(m.Payload) enc.AddEmbeddedJSONKey("Payload", &payloadSlice) } // IsNil checks if instance is nil func (m *Message) IsNil() bool { return m == nil } // UnmarshalJSONObject implements gojay's UnmarshalerJSONObject func (m *Message) UnmarshalJSONObject(dec *gojay.Decoder, k string) error { switch k { case "Id": return dec.Int(&m.Id) case "Name": return dec.String(&m.Name) case "Price": return dec.Float64(&m.Price) case "Ints": var aSlice = Ints{} err := dec.Array(&aSlice) if err == nil && len(aSlice) > 0 { m.Ints = []int(aSlice) } return err case "Floats": var aSlice = Float64s{} err := dec.Array(&aSlice) if err == nil && len(aSlice) > 0 { m.Floats = []float64(aSlice) } return err case "SubMessageX": var value = SubMessagePool.Get().(*SubMessage) err := dec.Object(value) if err == nil { m.SubMessageX = value } return err case "MessagesX": var aSlice = SubMessagesPtr{} err := dec.Array(&aSlice) if err == nil && len(aSlice) > 0 { m.MessagesX = []*SubMessage(aSlice) } return err case "SubMessageY": err := dec.Object(&m.SubMessageY) return err case "MessagesY": var aSlice = SubMessages{} err := dec.Array(&aSlice) if err == nil && len(aSlice) > 0 { m.MessagesY = []SubMessage(aSlice) } return err case "IsTrue": var value bool err := dec.Bool(&value) if err == nil { m.IsTrue = &value } return err case "Payload": var value = gojay.EmbeddedJSON{} err := dec.AddEmbeddedJSON(&value) if err == nil && len(value) > 0 { m.Payload = []byte(value) } return err } return nil } // NKeys returns the number of keys to unmarshal func (m *Message) NKeys() int { return 11 } // Reset reset fields func (m *Message) Reset() { m.Id = 0 m.Name = "" m.Price = 0.0 m.Ints = nil m.Floats = nil SubMessagePool.Put(m.SubMessageX) m.SubMessageX = nil for i := range m.MessagesX { m.MessagesX[i].Reset() SubMessagePool.Put(m.MessagesX[i]) } m.MessagesX = nil for i := range m.MessagesY { m.MessagesY[i].Reset() SubMessagePool.Put(&m.MessagesY[i]) } m.MessagesY = nil m.IsTrue = nil m.Payload = nil } golang-github-francoispqt-gojay-1.2.13/gojay/codegen/test/pooled_struct/encoding_test.go000066400000000000000000000035411365331665100315350ustar00rootroot00000000000000package pooled_struct import ( "bytes" "github.com/francoispqt/gojay" "github.com/stretchr/testify/assert" "github.com/viant/assertly" "testing" ) func TestMessage_Unmarshal(t *testing.T) { input := `{ "Id": 1022, "Name": "name acc", "Price": 13.3, "Ints": [ 1, 2, 5 ], "Floats": [ 2.3, 4.6, 7.4 ], "SubMessageX": { "Id": 102, "Description": "abcd" }, "MessagesX": [ { "Id": 2102, "Description": "abce" } ], "SubMessageY": { "Id": 3102, "Description": "abcf" }, "MessagesY": [ { "Id": 5102, "Description": "abcg" }, { "Id": 5106, "Description": "abcgg" } ], "IsTrue": true, "Payload": "" }` var data = []byte(input) message := MessagePool.Get().(*Message) err := gojay.UnmarshalJSONObject(data, message) assert.Nil(t, err) message.Reset() MessagePool.Put(message) } func TestMessage_Marshal(t *testing.T) { input := `{ "Id": 1022, "Name": "name acc", "Price": 13.3, "Ints": [ 1, 2, 5 ], "Floats": [ 2.3, 4.6, 7.4 ], "SubMessageX": { "Id": 102, "Description": "abcd" }, "MessagesX": [ { "Id": 2102, "Description": "abce" } ], "SubMessageY": { "Id": 3102, "Description": "abcf" }, "MessagesY": [ { "Id": 5102, "Description": "abcg" }, { "Id": 5106, "Description": "abcgg" } ], "IsTrue": true, "Payload": "" }` var data = []byte(input) message := MessagePool.Get().(*Message) err := gojay.UnmarshalJSONObject(data, message) assert.Nil(t, err) defer func() { message.Reset() MessagePool.Put(message) }() var writer = new(bytes.Buffer) encoder := gojay.NewEncoder(writer) err = encoder.Encode(message) assert.Nil(t, err) var JSON = writer.String() assertly.AssertValues(t, input, JSON) } golang-github-francoispqt-gojay-1.2.13/gojay/codegen/test/pooled_struct/message.go000066400000000000000000000004401365331665100303270ustar00rootroot00000000000000package pooled_struct type Message struct { Id int Name string Price float64 Ints []int Floats []float64 SubMessageX *SubMessage MessagesX []*SubMessage SubMessageY SubMessage MessagesY []SubMessage IsTrue *bool Payload []byte } golang-github-francoispqt-gojay-1.2.13/gojay/codegen/test/pooled_struct/sub_message.go000066400000000000000000000002251365331665100312010ustar00rootroot00000000000000package pooled_struct import "time" type SubMessage struct { Id int Description string StartTime time.Time EndTime *time.Time } golang-github-francoispqt-gojay-1.2.13/gojay/gojay.go000066400000000000000000000013261365331665100225470ustar00rootroot00000000000000package main import ( "flag" "github.com/francoispqt/gojay/gojay/codegen" "log" ) var pkg = flag.String("pkg", "", "the package name of the generated file") var dst = flag.String("o", "", "destination file to output generated code") var src = flag.String("s", "", "source dir or file (absolute or relative path)") var types = flag.String("t", "", "types to generate") var annotation = flag.String("a", "json", "annotation tag (default json)") var poolObjects = flag.String("p", "", "generate code to reuse objects using sync.Pool") func main() { flag.Parse() options := codegen.NewOptionsWithFlagSet(flag.CommandLine) gen := codegen.NewGenerator(options) if err := gen.Generate(); err != nil { log.Fatal(err) } } golang-github-francoispqt-gojay-1.2.13/gojay_example_test.go000066400000000000000000000022601365331665100242060ustar00rootroot00000000000000package gojay_test import ( "fmt" "log" "os" "strings" "github.com/francoispqt/gojay" ) type User struct { ID int Name string Email string } func (u *User) UnmarshalJSONObject(dec *gojay.Decoder, k string) error { switch k { case "id": return dec.Int(&u.ID) case "name": return dec.String(&u.Name) case "email": return dec.String(&u.Email) } return nil } func (u *User) NKeys() int { return 3 } func (u *User) MarshalJSONObject(enc *gojay.Encoder) { enc.IntKey("id", u.ID) enc.StringKey("name", u.Name) enc.StringKey("email", u.Email) } func (u *User) IsNil() bool { return u == nil } func Example_decodeEncode() { reader := strings.NewReader(`{ "id": 1, "name": "John Doe", "email": "john.doe@email.com" }`) dec := gojay.BorrowDecoder(reader) defer dec.Release() u := &User{} err := dec.Decode(u) if err != nil { log.Fatal(err) } enc := gojay.BorrowEncoder(os.Stdout) err = enc.Encode(u) if err != nil { log.Fatal(err) } fmt.Printf("\nUser ID: %d\nName: %s\nEmail: %s\n", u.ID, u.Name, u.Email) // Output: // {"id":1,"name":"John Doe","email":"john.doe@email.com"} // User ID: 1 // Name: John Doe // Email: john.doe@email.com } golang-github-francoispqt-gojay-1.2.13/gojay_test.go000066400000000000000000000175701365331665100225050ustar00rootroot00000000000000package gojay type testObject struct { testStr string testStrNull *string testInt int testIntNull *int testInt64 int64 testInt64Null *int64 testInt32 int32 testInt32Null *int32 testInt16 int16 testInt16Null *int16 testInt8 int8 testInt8Null *int8 testUint64 uint64 testUint64Null *uint64 testUint32 uint32 testUint32Null *uint32 testUint16 uint16 testUint16Null *uint16 testUint8 uint8 testUint8Null *uint8 testFloat64 float64 testFloat64Null *float64 testFloat32 float32 testFloat32Null *float32 testBool bool testBoolNull *bool testSubObject *testObject testSubArray testSliceInts testInterface interface{} } // make sure it implements interfaces var _ MarshalerJSONObject = &testObject{} var _ UnmarshalerJSONObject = &testObject{} func (t *testObject) IsNil() bool { return t == nil } func (t *testObject) MarshalJSONObject(enc *Encoder) { enc.AddStringKey("testStr", t.testStr) enc.AddIntKey("testInt", t.testInt) enc.AddIntKey("testInt64", int(t.testInt64)) enc.AddIntKey("testInt32", int(t.testInt32)) enc.AddIntKey("testInt16", int(t.testInt16)) enc.AddIntKey("testInt8", int(t.testInt8)) enc.AddIntKey("testUint64", int(t.testUint64)) enc.AddIntKey("testUint32", int(t.testUint32)) enc.AddIntKey("testUint16", int(t.testUint16)) enc.AddIntKey("testUint8", int(t.testUint8)) enc.AddFloatKey("testFloat64", t.testFloat64) enc.AddFloat32Key("testFloat32", t.testFloat32) enc.AddBoolKey("testBool", t.testBool) } func (t *testObject) UnmarshalJSONObject(dec *Decoder, k string) error { switch k { case "testStr": return dec.AddString(&t.testStr) case "testStrNull": return dec.AddStringNull(&t.testStrNull) case "testInt": return dec.AddInt(&t.testInt) case "testIntNull": return dec.AddIntNull(&t.testIntNull) case "testInt64": return dec.AddInt64(&t.testInt64) case "testInt64Null": return dec.AddInt64Null(&t.testInt64Null) case "testInt32": return dec.AddInt32(&t.testInt32) case "testInt32Null": return dec.AddInt32Null(&t.testInt32Null) case "testInt16": return dec.AddInt16(&t.testInt16) case "testInt16Null": return dec.AddInt16Null(&t.testInt16Null) case "testInt8": return dec.AddInt8(&t.testInt8) case "testInt8Null": return dec.AddInt8Null(&t.testInt8Null) case "testUint64": return dec.AddUint64(&t.testUint64) case "testUint64Null": return dec.AddUint64Null(&t.testUint64Null) case "testUint32": return dec.AddUint32(&t.testUint32) case "testUint32Null": return dec.AddUint32Null(&t.testUint32Null) case "testUint16": return dec.AddUint16(&t.testUint16) case "testUint16Null": return dec.AddUint16Null(&t.testUint16Null) case "testUint8": return dec.AddUint8(&t.testUint8) case "testUint8Null": return dec.AddUint8Null(&t.testUint8Null) case "testFloat64": return dec.AddFloat(&t.testFloat64) case "testFloat64Null": return dec.AddFloatNull(&t.testFloat64Null) case "testFloat32": return dec.AddFloat32(&t.testFloat32) case "testFloat32Null": return dec.AddFloat32Null(&t.testFloat32Null) case "testBool": return dec.AddBool(&t.testBool) case "testBoolNull": return dec.AddBoolNull(&t.testBoolNull) case "testInterface": return dec.AddInterface(&t.testInterface) } return nil } func (t *testObject) NKeys() int { return 29 } type testObject0Keys struct { testStr string testInt int testInt64 int64 testInt32 int32 testInt16 int16 testInt8 int8 testUint64 uint64 testUint32 uint32 testUint16 uint16 testUint8 uint8 testFloat64 float64 testFloat32 float32 testBool bool testSubObject *testObject0Keys testSubArray testSliceInts testInterface interface{} } // make sure it implements interfaces var _ MarshalerJSONObject = &testObject0Keys{} var _ UnmarshalerJSONObject = &testObject0Keys{} func (t *testObject0Keys) IsNil() bool { return t == nil } func (t *testObject0Keys) MarshalJSONObject(enc *Encoder) { enc.AddStringKey("testStr", t.testStr) enc.AddIntKey("testInt", t.testInt) enc.AddIntKey("testInt64", int(t.testInt64)) enc.AddIntKey("testInt32", int(t.testInt32)) enc.AddIntKey("testInt16", int(t.testInt16)) enc.AddIntKey("testInt8", int(t.testInt8)) enc.AddIntKey("testUint64", int(t.testUint64)) enc.AddIntKey("testUint32", int(t.testUint32)) enc.AddIntKey("testUint16", int(t.testUint16)) enc.AddIntKey("testUint8", int(t.testUint8)) enc.AddFloatKey("testFloat64", t.testFloat64) enc.AddFloat32Key("testFloat32", t.testFloat32) enc.AddBoolKey("testBool", t.testBool) enc.AddInterfaceKey("testInterface", t.testInterface) } func (t *testObject0Keys) UnmarshalJSONObject(dec *Decoder, k string) error { switch k { case "testStr": return dec.AddString(&t.testStr) case "testInt": return dec.AddInt(&t.testInt) case "testInt64": return dec.AddInt64(&t.testInt64) case "testInt32": return dec.AddInt32(&t.testInt32) case "testInt16": return dec.AddInt16(&t.testInt16) case "testInt8": return dec.AddInt8(&t.testInt8) case "testUint64": return dec.AddUint64(&t.testUint64) case "testUint32": return dec.AddUint32(&t.testUint32) case "testUint16": return dec.AddUint16(&t.testUint16) case "testUint8": return dec.AddUint8(&t.testUint8) case "testFloat64": return dec.AddFloat(&t.testFloat64) case "testFloat32": return dec.AddFloat32(&t.testFloat32) case "testBool": return dec.AddBool(&t.testBool) case "testInterface": return dec.AddInterface(&t.testInterface) } return nil } func (t *testObject0Keys) NKeys() int { return 0 } type testObjectComplex struct { testSubObject *testObject testSubSliceInts *testSliceInts testStr string testSubObject2 *testObjectComplex } func (t *testObjectComplex) IsNil() bool { return t == nil } func (t *testObjectComplex) MarshalJSONObject(enc *Encoder) { enc.AddObjectKey("testSubObject", t.testSubObject) enc.AddStringKey("testStr", t.testStr) enc.AddObjectKey("testStr", t.testSubObject2) } func (t *testObjectComplex) UnmarshalJSONObject(dec *Decoder, k string) error { switch k { case "testSubObject": return dec.AddObject(t.testSubObject) case "testSubSliceInts": return dec.AddArray(t.testSubSliceInts) case "testStr": return dec.AddString(&t.testStr) case "testSubObject2": return dec.AddObject(t.testSubObject2) } return nil } func (t *testObjectComplex) NKeys() int { return 4 } // make sure it implements interfaces var _ MarshalerJSONObject = &testObjectComplex{} var _ UnmarshalerJSONObject = &testObjectComplex{} type TestObj struct { test int test2 int test3 string test4 string test5 float64 testArr testSliceObjects testSubObj *TestSubObj testSubObj2 *TestSubObj } type TestSubObj struct { test3 int test4 int test5 string testSubSubObj *TestSubObj testSubSubObj2 *TestSubObj } func (t *TestSubObj) UnmarshalJSONObject(dec *Decoder, key string) error { switch key { case "test": return dec.AddInt(&t.test3) case "test2": return dec.AddInt(&t.test4) case "test3": return dec.AddString(&t.test5) case "testSubSubObj": t.testSubSubObj = &TestSubObj{} return dec.AddObject(t.testSubSubObj) case "testSubSubObj2": t.testSubSubObj2 = &TestSubObj{} return dec.AddObject(t.testSubSubObj2) } return nil } func (t *TestSubObj) NKeys() int { return 0 } func (t *TestObj) UnmarshalJSONObject(dec *Decoder, key string) error { switch key { case "test": return dec.AddInt(&t.test) case "test2": return dec.AddInt(&t.test2) case "test3": return dec.AddString(&t.test3) case "test4": return dec.AddString(&t.test4) case "test5": return dec.AddFloat(&t.test5) case "testSubObj": t.testSubObj = &TestSubObj{} return dec.AddObject(t.testSubObj) case "testSubObj2": t.testSubObj2 = &TestSubObj{} return dec.AddObject(t.testSubObj2) case "testArr": return dec.AddArray(&t.testArr) } return nil } func (t *TestObj) NKeys() int { return 8 }