README.md (25340B)
1 [![Build Status](https://travis-ci.org/francoispqt/gojay.svg?branch=master)](https://travis-ci.org/francoispqt/gojay) 2 [![codecov](https://codecov.io/gh/francoispqt/gojay/branch/master/graph/badge.svg)](https://codecov.io/gh/francoispqt/gojay) 3 [![Go Report Card](https://goreportcard.com/badge/github.com/francoispqt/gojay)](https://goreportcard.com/report/github.com/francoispqt/gojay) 4 [![Go doc](http://img.shields.io/badge/go-documentation-blue.svg?style=flat-square 5 )](https://godoc.org/github.com/francoispqt/gojay) 6 ![MIT License](https://img.shields.io/badge/license-mit-blue.svg?style=flat-square) 7 [![Sourcegraph](https://sourcegraph.com/github.com/francoispqt/gojay/-/badge.svg)](https://sourcegraph.com/github.com/francoispqt/gojay) 8 ![stability-stable](https://img.shields.io/badge/stability-stable-green.svg) 9 10 # GoJay 11 12 <img src="https://github.com/francoispqt/gojay/raw/master/gojay.png" width="200px"> 13 14 GoJay is a performant JSON encoder/decoder for Golang (currently the most performant, [see benchmarks](#benchmark-results)). 15 16 It has a simple API and doesn't use reflection. It relies on small interfaces to decode/encode structures and slices. 17 18 Gojay also comes with powerful stream decoding features and an even faster [Unsafe](#unsafe-api) API. 19 20 There is also a [code generation tool](https://github.com/francoispqt/gojay/tree/master/gojay) to make usage easier and faster. 21 22 # Why another JSON parser? 23 24 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. 25 26 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. 27 28 This is how GoJay aims to be a very fast, JIT stream parser with 0 reflection, low allocation with a friendly API. 29 30 # Get started 31 32 ```bash 33 go get github.com/francoispqt/gojay 34 ``` 35 36 * [Encoder](#encoding) 37 * [Decoder](#decoding) 38 * [Stream API](#stream-api) 39 * [Code Generation](https://github.com/francoispqt/gojay/tree/master/gojay) 40 41 ## Decoding 42 43 Decoding is done through two different API similar to standard `encoding/json`: 44 * [Unmarshal](#unmarshal-api) 45 * [Decode](#decode-api) 46 47 48 Example of basic stucture decoding with Unmarshal: 49 ```go 50 import "go.lair.cx/gojay" 51 52 type user struct { 53 id int 54 name string 55 email string 56 } 57 // implement gojay.UnmarshalerJSONObject 58 func (u *user) UnmarshalJSONObject(dec *gojay.Decoder, key string) error { 59 switch key { 60 case "id": 61 return dec.Int(&u.id) 62 case "name": 63 return dec.String(&u.name) 64 case "email": 65 return dec.String(&u.email) 66 } 67 return nil 68 } 69 func (u *user) NKeys() int { 70 return 3 71 } 72 73 func main() { 74 u := &user{} 75 d := []byte(`{"id":1,"name":"gojay","email":"gojay@email.com"}`) 76 err := gojay.UnmarshalJSONObject(d, u) 77 if err != nil { 78 log.Fatal(err) 79 } 80 } 81 ``` 82 83 with Decode: 84 ```go 85 func main() { 86 u := &user{} 87 dec := gojay.NewDecoder(bytes.NewReader([]byte(`{"id":1,"name":"gojay","email":"gojay@email.com"}`))) 88 err := dec.DecodeObject(d, u) 89 if err != nil { 90 log.Fatal(err) 91 } 92 } 93 ``` 94 95 ### Unmarshal API 96 97 Unmarshal API decodes a `[]byte` to a given pointer with a single function. 98 99 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. 100 101 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 {}`. 102 103 Unmarshal API comes with three functions: 104 * Unmarshal 105 ```go 106 func Unmarshal(data []byte, v interface{}) error 107 ``` 108 109 * UnmarshalJSONObject 110 ```go 111 func UnmarshalJSONObject(data []byte, v gojay.UnmarshalerJSONObject) error 112 ``` 113 114 * UnmarshalJSONArray 115 ```go 116 func UnmarshalJSONArray(data []byte, v gojay.UnmarshalerJSONArray) error 117 ``` 118 119 120 ### Decode API 121 122 Decode API decodes a `[]byte` to a given pointer by creating or borrowing a `*gojay.Decoder` with an `io.Reader` and calling `Decode` methods. 123 124 __Getting a *gojay.Decoder or Borrowing__ 125 126 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)`. 127 128 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. 129 130 Example getting a fresh an releasing: 131 ```go 132 str := "" 133 dec := gojay.NewDecoder(strings.NewReader(`"test"`)) 134 defer dec.Release() 135 if err := dec.Decode(&str); err != nil { 136 log.Fatal(err) 137 } 138 ``` 139 Example borrowing a decoder and releasing: 140 ```go 141 str := "" 142 dec := gojay.BorrowDecoder(strings.NewReader(`"test"`)) 143 defer dec.Release() 144 if err := dec.Decode(&str); err != nil { 145 log.Fatal(err) 146 } 147 ``` 148 149 `*gojay.Decoder` has multiple methods to decode to specific types: 150 * Decode 151 ```go 152 func (dec *gojay.Decoder) Decode(v interface{}) error 153 ``` 154 * DecodeObject 155 ```go 156 func (dec *gojay.Decoder) DecodeObject(v gojay.UnmarshalerJSONObject) error 157 ``` 158 * DecodeArray 159 ```go 160 func (dec *gojay.Decoder) DecodeArray(v gojay.UnmarshalerJSONArray) error 161 ``` 162 * DecodeInt 163 ```go 164 func (dec *gojay.Decoder) DecodeInt(v *int) error 165 ``` 166 * DecodeBool 167 ```go 168 func (dec *gojay.Decoder) DecodeBool(v *bool) error 169 ``` 170 * DecodeString 171 ```go 172 func (dec *gojay.Decoder) DecodeString(v *string) error 173 ``` 174 175 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. 176 177 Example: 178 ```go 179 reader := strings.NewReader(`"John Doe"`) 180 dec := NewDecoder(reader) 181 182 var str string 183 err := dec.DecodeString(&str) 184 if err != nil { 185 log.Fatal(err) 186 } 187 188 fmt.Println(str) // John Doe 189 ``` 190 191 ### Structs and Maps 192 #### UnmarshalerJSONObject Interface 193 194 To unmarshal a JSON object to a structure, the structure must implement the `UnmarshalerJSONObject` interface: 195 ```go 196 type UnmarshalerJSONObject interface { 197 UnmarshalJSONObject(*gojay.Decoder, string) error 198 NKeys() int 199 } 200 ``` 201 `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. 202 203 `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. 204 205 Example of implementation for a struct: 206 ```go 207 type user struct { 208 id int 209 name string 210 email string 211 } 212 // implement UnmarshalerJSONObject 213 func (u *user) UnmarshalJSONObject(dec *gojay.Decoder, key string) error { 214 switch key { 215 case "id": 216 return dec.Int(&u.id) 217 case "name": 218 return dec.String(&u.name) 219 case "email": 220 return dec.String(&u.email) 221 } 222 return nil 223 } 224 func (u *user) NKeys() int { 225 return 3 226 } 227 ``` 228 229 Example of implementation for a `map[string]string`: 230 ```go 231 // define our custom map type implementing UnmarshalerJSONObject 232 type message map[string]string 233 234 // Implementing Unmarshaler 235 func (m message) UnmarshalJSONObject(dec *gojay.Decoder, k string) error { 236 str := "" 237 err := dec.String(&str) 238 if err != nil { 239 return err 240 } 241 m[k] = str 242 return nil 243 } 244 245 // we return 0, it tells the Decoder to decode all keys 246 func (m message) NKeys() int { 247 return 0 248 } 249 ``` 250 251 ### Arrays, Slices and Channels 252 253 To unmarshal a JSON object to a slice an array or a channel, it must implement the UnmarshalerJSONArray interface: 254 ```go 255 type UnmarshalerJSONArray interface { 256 UnmarshalJSONArray(*gojay.Decoder) error 257 } 258 ``` 259 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. 260 261 Example of implementation with a slice: 262 ```go 263 type testSlice []string 264 // implement UnmarshalerJSONArray 265 func (t *testSlice) UnmarshalJSONArray(dec *gojay.Decoder) error { 266 str := "" 267 if err := dec.String(&str); err != nil { 268 return err 269 } 270 *t = append(*t, str) 271 return nil 272 } 273 274 func main() { 275 dec := gojay.BorrowDecoder(strings.NewReader(`["Tom", "Jim"]`)) 276 var slice testSlice 277 err := dec.DecodeArray(&slice) 278 if err != nil { 279 log.Fatal(err) 280 } 281 fmt.Println(slice) // [Tom Jim] 282 dec.Release() 283 } 284 ``` 285 286 Example of implementation with a channel: 287 ```go 288 type testChannel chan string 289 // implement UnmarshalerJSONArray 290 func (c testChannel) UnmarshalJSONArray(dec *gojay.Decoder) error { 291 str := "" 292 if err := dec.String(&str); err != nil { 293 return err 294 } 295 c <- str 296 return nil 297 } 298 299 func main() { 300 dec := gojay.BorrowDecoder(strings.NewReader(`["Tom", "Jim"]`)) 301 c := make(testChannel, 2) 302 err := dec.DecodeArray(c) 303 if err != nil { 304 log.Fatal(err) 305 } 306 for i := 0; i < 2; i++ { 307 fmt.Println(<-c) 308 } 309 close(c) 310 dec.Release() 311 } 312 ``` 313 314 Example of implementation with an array: 315 ```go 316 type testArray [3]string 317 // implement UnmarshalerJSONArray 318 func (a *testArray) UnmarshalJSONArray(dec *Decoder) error { 319 var str string 320 if err := dec.String(&str); err != nil { 321 return err 322 } 323 a[dec.Index()] = str 324 return nil 325 } 326 327 func main() { 328 dec := gojay.BorrowDecoder(strings.NewReader(`["Tom", "Jim", "Bob"]`)) 329 var a testArray 330 err := dec.DecodeArray(&a) 331 fmt.Println(a) // [Tom Jim Bob] 332 dec.Release() 333 } 334 ``` 335 336 ### Other types 337 To decode other types (string, int, int32, int64, uint32, uint64, float, booleans), you don't need to implement any interface. 338 339 Example of encoding strings: 340 ```go 341 func main() { 342 json := []byte(`"Jay"`) 343 var v string 344 err := gojay.Unmarshal(json, &v) 345 if err != nil { 346 log.Fatal(err) 347 } 348 fmt.Println(v) // Jay 349 } 350 ``` 351 352 ### Decode values methods 353 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. 354 355 Non exhaustive list of methods available (to see all methods, check the godoc): 356 ```go 357 dec.Int 358 dec.Int8 359 dec.Int16 360 dec.Int32 361 dec.Int64 362 dec.Uint8 363 dec.Uint16 364 dec.Uint32 365 dec.Uint64 366 dec.String 367 dec.Time 368 dec.Bool 369 dec.SQLNullString 370 dec.SQLNullInt64 371 ``` 372 373 374 ## Encoding 375 376 Encoding is done through two different API similar to standard `encoding/json`: 377 * [Marshal](#marshal-api) 378 * [Encode](#encode-api) 379 380 Example of basic structure encoding with Marshal: 381 ```go 382 import "go.lair.cx/gojay" 383 384 type user struct { 385 id int 386 name string 387 email string 388 } 389 390 // implement MarshalerJSONObject 391 func (u *user) MarshalJSONObject(enc *gojay.Encoder) { 392 enc.IntKey("id", u.id) 393 enc.StringKey("name", u.name) 394 enc.StringKey("email", u.email) 395 } 396 func (u *user) IsNil() bool { 397 return u == nil 398 } 399 400 func main() { 401 u := &user{1, "gojay", "gojay@email.com"} 402 b, err := gojay.MarshalJSONObject(u) 403 if err != nil { 404 log.Fatal(err) 405 } 406 fmt.Println(string(b)) // {"id":1,"name":"gojay","email":"gojay@email.com"} 407 } 408 ``` 409 410 with Encode: 411 ```go 412 func main() { 413 u := &user{1, "gojay", "gojay@email.com"} 414 b := strings.Builder{} 415 enc := gojay.NewEncoder(&b) 416 if err := enc.Encode(u); err != nil { 417 log.Fatal(err) 418 } 419 fmt.Println(b.String()) // {"id":1,"name":"gojay","email":"gojay@email.com"} 420 } 421 ``` 422 423 ### Marshal API 424 425 Marshal API encodes a value to a JSON `[]byte` with a single function. 426 427 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. 428 429 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 {}`. 430 431 Marshal API comes with three functions: 432 * Marshal 433 ```go 434 func Marshal(v interface{}) ([]byte, error) 435 ``` 436 437 * MarshalJSONObject 438 ```go 439 func MarshalJSONObject(v gojay.MarshalerJSONObject) ([]byte, error) 440 ``` 441 442 * MarshalJSONArray 443 ```go 444 func MarshalJSONArray(v gojay.MarshalerJSONArray) ([]byte, error) 445 ``` 446 447 ### Encode API 448 449 Encode API decodes a value to JSON by creating or borrowing a `*gojay.Encoder` sending it to an `io.Writer` and calling `Encode` methods. 450 451 __Getting a *gojay.Encoder or Borrowing__ 452 453 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)`. 454 455 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. 456 457 Example getting a fresh encoder an releasing: 458 ```go 459 str := "test" 460 b := strings.Builder{} 461 enc := gojay.NewEncoder(&b) 462 defer enc.Release() 463 if err := enc.Encode(str); err != nil { 464 log.Fatal(err) 465 } 466 ``` 467 Example borrowing an encoder and releasing: 468 ```go 469 str := "test" 470 b := strings.Builder{} 471 enc := gojay.BorrowEncoder(b) 472 defer enc.Release() 473 if err := enc.Encode(str); err != nil { 474 log.Fatal(err) 475 } 476 ``` 477 478 `*gojay.Encoder` has multiple methods to encoder specific types to JSON: 479 * Encode 480 ```go 481 func (enc *gojay.Encoder) Encode(v interface{}) error 482 ``` 483 * EncodeObject 484 ```go 485 func (enc *gojay.Encoder) EncodeObject(v gojay.MarshalerJSONObject) error 486 ``` 487 * EncodeArray 488 ```go 489 func (enc *gojay.Encoder) EncodeArray(v gojay.MarshalerJSONArray) error 490 ``` 491 * EncodeInt 492 ```go 493 func (enc *gojay.Encoder) EncodeInt(n int) error 494 ``` 495 * EncodeInt64 496 ```go 497 func (enc *gojay.Encoder) EncodeInt64(n int64) error 498 ``` 499 * EncodeFloat 500 ```go 501 func (enc *gojay.Encoder) EncodeFloat(n float64) error 502 ``` 503 * EncodeBool 504 ```go 505 func (enc *gojay.Encoder) EncodeBool(v bool) error 506 ``` 507 * EncodeString 508 ```go 509 func (enc *gojay.Encoder) EncodeString(s string) error 510 ``` 511 512 ### Structs and Maps 513 514 To encode a structure, the structure must implement the MarshalerJSONObject interface: 515 ```go 516 type MarshalerJSONObject interface { 517 MarshalJSONObject(enc *gojay.Encoder) 518 IsNil() bool 519 } 520 ``` 521 `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. 522 523 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. 524 525 Example of implementation for a struct: 526 ```go 527 type user struct { 528 id int 529 name string 530 email string 531 } 532 533 // implement MarshalerJSONObject 534 func (u *user) MarshalJSONObject(enc *gojay.Encoder) { 535 enc.IntKey("id", u.id) 536 enc.StringKey("name", u.name) 537 enc.StringKey("email", u.email) 538 } 539 func (u *user) IsNil() bool { 540 return u == nil 541 } 542 ``` 543 544 Example of implementation for a `map[string]string`: 545 ```go 546 // define our custom map type implementing MarshalerJSONObject 547 type message map[string]string 548 549 // Implementing Marshaler 550 func (m message) MarshalJSONObject(enc *gojay.Encoder) { 551 for k, v := range m { 552 enc.StringKey(k, v) 553 } 554 } 555 556 func (m message) IsNil() bool { 557 return m == nil 558 } 559 ``` 560 561 ### Arrays and Slices 562 To encode an array or a slice, the slice/array must implement the MarshalerJSONArray interface: 563 ```go 564 type MarshalerJSONArray interface { 565 MarshalJSONArray(enc *gojay.Encoder) 566 IsNil() bool 567 } 568 ``` 569 `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. 570 571 `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. 572 573 Example of implementation: 574 ```go 575 type users []*user 576 // implement MarshalerJSONArray 577 func (u *users) MarshalJSONArray(enc *gojay.Encoder) { 578 for _, e := range u { 579 enc.Object(e) 580 } 581 } 582 func (u *users) IsNil() bool { 583 return len(u) == 0 584 } 585 ``` 586 587 ### Other types 588 To encode other types (string, int, float, booleans), you don't need to implement any interface. 589 590 Example of encoding strings: 591 ```go 592 func main() { 593 name := "Jay" 594 b, err := gojay.Marshal(name) 595 if err != nil { 596 log.Fatal(err) 597 } 598 fmt.Println(string(b)) // "Jay" 599 } 600 ``` 601 602 # Stream API 603 604 ### Stream Decoding 605 GoJay ships with a powerful stream decoder. 606 607 It allows to read continuously from an io.Reader stream and do JIT decoding writing unmarshalled JSON to a channel to allow async consuming. 608 609 When using the Stream API, the Decoder implements context.Context to provide graceful cancellation. 610 611 To decode a stream of JSON, you must call `gojay.Stream.DecodeStream` and pass it a `UnmarshalerStream` implementation. 612 613 ```go 614 type UnmarshalerStream interface { 615 UnmarshalStream(*StreamDecoder) error 616 } 617 ``` 618 619 Example of implementation of stream reading from a WebSocket connection: 620 ```go 621 // implement UnmarshalerStream 622 type ChannelStream chan *user 623 624 func (c ChannelStream) UnmarshalStream(dec *gojay.StreamDecoder) error { 625 u := &user{} 626 if err := dec.Object(u); err != nil { 627 return err 628 } 629 c <- u 630 return nil 631 } 632 633 func main() { 634 // get our websocket connection 635 origin := "http://localhost/" 636 url := "ws://localhost:12345/ws" 637 ws, err := websocket.Dial(url, "", origin) 638 if err != nil { 639 log.Fatal(err) 640 } 641 // create our channel which will receive our objects 642 streamChan := ChannelStream(make(chan *user)) 643 // borrow a decoder 644 dec := gojay.Stream.BorrowDecoder(ws) 645 // start decoding, it will block until a JSON message is decoded from the WebSocket 646 // or until Done channel is closed 647 go dec.DecodeStream(streamChan) 648 for { 649 select { 650 case v := <-streamChan: 651 // Got something from my websocket! 652 log.Println(v) 653 case <-dec.Done(): 654 log.Println("finished reading from WebSocket") 655 os.Exit(0) 656 } 657 } 658 } 659 ``` 660 661 ### Stream Encoding 662 GoJay ships with a powerful stream encoder part of the Stream API. 663 664 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. 665 666 When using the Stream API, the Encoder implements context.Context to provide graceful cancellation. 667 668 To encode a stream of data, you must call `EncodeStream` and pass it a `MarshalerStream` implementation. 669 670 ```go 671 type MarshalerStream interface { 672 MarshalStream(enc *gojay.StreamEncoder) 673 } 674 ``` 675 676 Example of implementation of stream writing to a WebSocket: 677 ```go 678 // Our structure which will be pushed to our stream 679 type user struct { 680 id int 681 name string 682 email string 683 } 684 685 func (u *user) MarshalJSONObject(enc *gojay.Encoder) { 686 enc.IntKey("id", u.id) 687 enc.StringKey("name", u.name) 688 enc.StringKey("email", u.email) 689 } 690 func (u *user) IsNil() bool { 691 return u == nil 692 } 693 694 // Our MarshalerStream implementation 695 type StreamChan chan *user 696 697 func (s StreamChan) MarshalStream(enc *gojay.StreamEncoder) { 698 select { 699 case <-enc.Done(): 700 return 701 case o := <-s: 702 enc.Object(o) 703 } 704 } 705 706 // Our main function 707 func main() { 708 // get our websocket connection 709 origin := "http://localhost/" 710 url := "ws://localhost:12345/ws" 711 ws, err := websocket.Dial(url, "", origin) 712 if err != nil { 713 log.Fatal(err) 714 } 715 // we borrow an encoder set stdout as the writer, 716 // set the number of consumer to 10 717 // and tell the encoder to separate each encoded element 718 // added to the channel by a new line character 719 enc := gojay.Stream.BorrowEncoder(ws).NConsumer(10).LineDelimited() 720 // instantiate our MarshalerStream 721 s := StreamChan(make(chan *user)) 722 // start the stream encoder 723 // will block its goroutine until enc.Cancel(error) is called 724 // or until something is written to the channel 725 go enc.EncodeStream(s) 726 // write to our MarshalerStream 727 for i := 0; i < 1000; i++ { 728 s <- &user{i, "username", "user@email.com"} 729 } 730 // Wait 731 <-enc.Done() 732 } 733 ``` 734 735 # Unsafe API 736 737 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. 738 739 If you are not sure if your JSON is valid, don't use the Unsafe API. 740 741 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 :) 742 743 Access the `Unsafe` API this way: 744 ```go 745 gojay.Unsafe.Unmarshal(b, v) 746 ``` 747 748 749 # Benchmarks 750 751 Benchmarks encode and decode three different data based on size (small, medium, large). 752 753 To run benchmark for decoder: 754 ```bash 755 cd $GOPATH/src/github.com/francoispqt/gojay/benchmarks/decoder && make bench 756 ``` 757 758 To run benchmark for encoder: 759 ```bash 760 cd $GOPATH/src/github.com/francoispqt/gojay/benchmarks/encoder && make bench 761 ``` 762 763 # Benchmark Results 764 ## Decode 765 766 <img src="https://images2.imgbox.com/78/01/49OExcPh_o.png" width="500px"> 767 768 ### Small Payload 769 [benchmark code is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/decoder/decoder_bench_small_test.go) 770 771 [benchmark data is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/benchmarks_small.go) 772 773 | | ns/op | bytes/op | allocs/op | 774 |-----------------|-----------|--------------|-----------| 775 | Std Library | 2547 | 496 | 4 | 776 | JsonIter | 2046 | 312 | 12 | 777 | JsonParser | 1408 | 0 | 0 | 778 | EasyJson | 929 | 240 | 2 | 779 | **GoJay** | **807** | **256** | **2** | 780 | **GoJay-unsafe**| **712** | **112** | **1** | 781 782 ### Medium Payload 783 [benchmark code is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/decoder/decoder_bench_medium_test.go) 784 785 [benchmark data is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/benchmarks_medium.go) 786 787 | | ns/op | bytes/op | allocs/op | 788 |-----------------|-----------|----------|-----------| 789 | Std Library | 30148 | 2152 | 496 | 790 | JsonIter | 16309 | 2976 | 80 | 791 | JsonParser | 7793 | 0 | 0 | 792 | EasyJson | 7957 | 232 | 6 | 793 | **GoJay** | **4984** | **2448** | **8** | 794 | **GoJay-unsafe**| **4809** | **144** | **7** | 795 796 ### Large Payload 797 [benchmark code is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/decoder/decoder_bench_large_test.go) 798 799 [benchmark data is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/benchmarks_large.go) 800 801 | | ns/op | bytes/op | allocs/op | 802 |-----------------|-----------|-------------|-----------| 803 | JsonIter | 210078 | 41712 | 1136 | 804 | EasyJson | 106626 | 160 | 2 | 805 | JsonParser | 66813 | 0 | 0 | 806 | **GoJay** | **52153** | **31241** | **77** | 807 | **GoJay-unsafe**| **48277** | **2561** | **76** | 808 809 ## Encode 810 811 <img src="https://images2.imgbox.com/e9/cc/pnM8c7Gf_o.png" width="500px"> 812 813 ### Small Struct 814 [benchmark code is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/encoder/encoder_bench_small_test.go) 815 816 [benchmark data is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/benchmarks_small.go) 817 818 | | ns/op | bytes/op | allocs/op | 819 |----------------|----------|--------------|-----------| 820 | Std Library | 1280 | 464 | 3 | 821 | EasyJson | 871 | 944 | 6 | 822 | JsonIter | 866 | 272 | 3 | 823 | **GoJay** | **543** | **112** | **1** | 824 | **GoJay-func** | **347** | **0** | **0** | 825 826 ### Medium Struct 827 [benchmark code is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/encoder/encoder_bench_medium_test.go) 828 829 [benchmark data is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/benchmarks_medium.go) 830 831 | | ns/op | bytes/op | allocs/op | 832 |-------------|----------|--------------|-----------| 833 | Std Library | 5006 | 1496 | 25 | 834 | JsonIter | 2232 | 1544 | 20 | 835 | EasyJson | 1997 | 1544 | 19 | 836 | **GoJay** | **1522** | **312** | **14** | 837 838 ### Large Struct 839 [benchmark code is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/encoder/encoder_bench_large_test.go) 840 841 [benchmark data is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/benchmarks_large.go) 842 843 | | ns/op | bytes/op | allocs/op | 844 |-------------|-----------|--------------|-----------| 845 | Std Library | 66441 | 20576 | 332 | 846 | JsonIter | 35247 | 20255 | 328 | 847 | EasyJson | 32053 | 15474 | 327 | 848 | **GoJay** | **27847** | **9802** | **318** | 849 850 # Contributing 851 852 Contributions are welcome :) 853 854 If you encounter issues please report it in Github and/or send an email at [francois@parquet.ninja](mailto:francois@parquet.ninja) 855