diff --git a/helpers.go b/helpers.go index 04ac635..447e492 100644 --- a/helpers.go +++ b/helpers.go @@ -6,6 +6,7 @@ import ( "io/ioutil" "net/http" "strconv" + "unsafe" "github.com/mailru/easyjson/jlexer" "github.com/mailru/easyjson/jwriter" @@ -36,9 +37,17 @@ type UnknownsMarshaler interface { MarshalUnknowns(w *jwriter.Writer, first bool) } +func isNilInterface(i interface{}) bool { + return (*[2]uintptr)(unsafe.Pointer(&i))[1] == 0 +} + // Marshal returns data as a single byte slice. Method is suboptimal as the data is likely to be copied // from a chain of smaller chunks. func Marshal(v Marshaler) ([]byte, error) { + if isNilInterface(v) { + return nullBytes, nil + } + w := jwriter.Writer{} v.MarshalEasyJSON(&w) return w.BuildBytes() @@ -46,6 +55,10 @@ func Marshal(v Marshaler) ([]byte, error) { // MarshalToWriter marshals the data to an io.Writer. func MarshalToWriter(v Marshaler, w io.Writer) (written int, err error) { + if isNilInterface(v) { + return w.Write(nullBytes) + } + jw := jwriter.Writer{} v.MarshalEasyJSON(&jw) return jw.DumpTo(w) @@ -56,6 +69,13 @@ func MarshalToWriter(v Marshaler, w io.Writer) (written int, err error) { // false if an error occurred before any http.ResponseWriter methods were actually // invoked (in this case a 500 reply is possible). func MarshalToHTTPResponseWriter(v Marshaler, w http.ResponseWriter) (started bool, written int, err error) { + if isNilInterface(v) { + w.Header().Set("Content-Type", "application/json") + w.Header().Set("Content-Length", strconv.Itoa(len(nullBytes))) + written, err = w.Write(nullBytes) + return true, written, err + } + jw := jwriter.Writer{} v.MarshalEasyJSON(&jw) if jw.Error != nil { diff --git a/helpers_test.go b/helpers_test.go new file mode 100644 index 0000000..a10a46c --- /dev/null +++ b/helpers_test.go @@ -0,0 +1,12 @@ +package easyjson + +import "testing" + +func BenchmarkNilCheck(b *testing.B) { + var a *int + for i := 0; i < b.N; i++ { + if !isNilInterface(a) { + b.Fatal("expected it to be nil") + } + } +} diff --git a/tests/basic_test.go b/tests/basic_test.go index edf9bf2..e6872e4 100644 --- a/tests/basic_test.go +++ b/tests/basic_test.go @@ -1,6 +1,8 @@ package tests import ( + "bytes" + "net/http/httptest" "reflect" "testing" @@ -267,3 +269,35 @@ func TestMethodsNoGenerated(t *testing.T) { } } } + +func TestNil(t *testing.T) { + var p *PrimitiveTypes + + data, err := easyjson.Marshal(p) + if err != nil { + t.Errorf("easyjson.Marshal() error: %v", err) + } + if string(data) != "null" { + t.Errorf("Wanted null, got %q", string(data)) + } + + var b bytes.Buffer + if n, err := easyjson.MarshalToWriter(p, &b); err != nil || n != 4 { + t.Errorf("easyjson.MarshalToWriter() error: %v, written %d", err, n) + } + + if s := b.String(); s != "null" { + t.Errorf("Wanted null, got %q", s) + } + + w := httptest.NewRecorder() + started, written, err := easyjson.MarshalToHTTPResponseWriter(p, w) + if !started || written != 4 || err != nil { + t.Errorf("easyjson.MarshalToHTTPResponseWriter() error: %v, written %d, started %t", + err, written, started) + } + + if s := w.Body.String(); s != "null" { + t.Errorf("Wanted null, got %q", s) + } +}