diff --git a/README.md b/README.md index 84dd237..384f72c 100644 --- a/README.md +++ b/README.md @@ -141,11 +141,18 @@ for more information. when doing case-insensitive key matching. In the future, case-insensitive object key matching may be provided via an option to the generator. -* easyjson makes use of `unsafe`. While a "safe" version of easyjson could - be written, `unsafe` simplifies the code and provides significant performance - benefits by allowing no-copy conversion from `[]byte` to `string`. That said, - `unsafe` is used only when unmarshaling and parsing JSON, and any `unsafe` - operations / memory allocations done will be safely deallocated by easyjson. +* easyjson makes use of `unsafe`, which simplifies the code and + provides significant performance benefits by allowing no-copy + conversion from `[]byte` to `string`. That said, `unsafe` is used + only when unmarshaling and parsing JSON, and any `unsafe` operations + / memory allocations done will be safely deallocated by + easyjson. Set the build tag `easyjson_nounsafe` to compile it + without `unsafe`. + +* easyjson is compatible with Google App Engine. The `appengine` build + tag (set by App Engine's environment) will automatically disable the + use of `unsafe`, which is not allowed in App Engine's Standard + Environment. Note that the use with App Engine is still experimental. * Floats are formatted using the default precision from Go's `strconv` package. As such, easyjson will not correctly handle high precision floats when @@ -165,7 +172,8 @@ for more information. ## Benchmarks -Most benchmarks were done using the example [13kB example JSON](https://dev.twitter.com/rest/reference/get/search/tweets) +Most benchmarks were done using the example +[13kB example JSON](https://dev.twitter.com/rest/reference/get/search/tweets) (9k after eliminating whitespace). This example is similar to real-world data, is well-structured, and contains a healthy variety of different types, making it ideal for JSON serialization benchmarks. @@ -178,6 +186,9 @@ Note: * For large request marshaling benchmarks, a struct containing 50 regular samples was used, making a ~500kB output JSON. +* Benchmarks are showing the results of easyjson's default behaviour, + which makes use of `unsafe`. + Benchmarks are available in the repository and can be run by invoking `make`. ### easyjson vs. encoding/json diff --git a/benchmark/data_codec.go b/benchmark/data_codec.go index 0bb8c4c..d2d83fa 100644 --- a/benchmark/data_codec.go +++ b/benchmark/data_codec.go @@ -1,4 +1,6 @@ //+build use_codec +//+build !easyjson_nounsafe +//+build !appengine // ************************************************************ // DO NOT EDIT. @@ -10,10 +12,11 @@ package benchmark import ( "errors" "fmt" - codec1978 "github.com/ugorji/go/codec" "reflect" "runtime" "unsafe" + + codec1978 "github.com/ugorji/go/codec" ) const ( diff --git a/jlexer/bytestostr.go b/jlexer/bytestostr.go new file mode 100644 index 0000000..2ed8042 --- /dev/null +++ b/jlexer/bytestostr.go @@ -0,0 +1,24 @@ +// This file will only be included to the build if neither +// easyjson_nounsafe nor appengine build tag is set. See README notes +// for more details. + +//+build !easyjson_nounsafe +//+build !appengine + +package jlexer + +import ( + "reflect" + "unsafe" +) + +// bytesToStr creates a string pointing at the slice to avoid copying. +// +// Warning: the string returned by the function should be used with care, as the whole input data +// chunk may be either blocked from being freed by GC because of a single string or the buffer.Data +// may be garbage-collected even when the string exists. +func bytesToStr(data []byte) string { + h := (*reflect.SliceHeader)(unsafe.Pointer(&data)) + shdr := reflect.StringHeader{h.Data, h.Len} + return *(*string)(unsafe.Pointer(&shdr)) +} diff --git a/jlexer/bytestostr_nounsafe.go b/jlexer/bytestostr_nounsafe.go new file mode 100644 index 0000000..864d1be --- /dev/null +++ b/jlexer/bytestostr_nounsafe.go @@ -0,0 +1,13 @@ +// This file is included to the build if any of the buildtags below +// are defined. Refer to README notes for more details. + +//+build easyjson_nounsafe appengine + +package jlexer + +// bytesToStr creates a string normally from []byte +// +// Note that this method is roughly 1.5x slower than using the 'unsafe' method. +func bytesToStr(data []byte) string { + return string(data) +} diff --git a/jlexer/lexer.go b/jlexer/lexer.go index c8a5d07..ae8344e 100644 --- a/jlexer/lexer.go +++ b/jlexer/lexer.go @@ -9,12 +9,10 @@ import ( "errors" "fmt" "io" - "reflect" "strconv" "unicode" "unicode/utf16" "unicode/utf8" - "unsafe" ) // tokenKind determines type of a token. @@ -205,17 +203,6 @@ func (r *Lexer) fetchFalse() { } } -// bytesToStr creates a string pointing at the slice to avoid copying. -// -// Warning: the string returned by the function should be used with care, as the whole input data -// chunk may be either blocked from being freed by GC because of a single string or the buffer.Data -// may be garbage-collected even when the string exists. -func bytesToStr(data []byte) string { - h := (*reflect.SliceHeader)(unsafe.Pointer(&data)) - shdr := reflect.StringHeader{h.Data, h.Len} - return *(*string)(unsafe.Pointer(&shdr)) -} - // fetchNumber scans a number literal token. func (r *Lexer) fetchNumber() { hasE := false