commit c59a4b578d512909a38fada3747ff978bc79be28
parent 3d991e95a8197d6bc05e10dff748d8a0464784e9
Author: francoispqt <francois@parquet.ninja>
Date: Fri, 27 Apr 2018 00:03:41 +0800
make unmarshal api safer by copying initial buffer and adding an Unsafe api
Diffstat:
5 files changed, 142 insertions(+), 4 deletions(-)
diff --git a/benchmarks/decoder/decoder_bench_large_test.go b/benchmarks/decoder/decoder_bench_large_test.go
@@ -49,3 +49,11 @@ func BenchmarkGoJayDecodeObjLarge(b *testing.B) {
gojay.UnmarshalObject(benchmarks.LargeFixture, &result)
}
}
+
+func BenchmarkGoJayUnsafeDecodeObjLarge(b *testing.B) {
+ b.ReportAllocs()
+ for i := 0; i < b.N; i++ {
+ result := benchmarks.LargePayload{}
+ gojay.Unsafe.UnmarshalObject(benchmarks.LargeFixture, &result)
+ }
+}
diff --git a/benchmarks/decoder/decoder_bench_medium_test.go b/benchmarks/decoder/decoder_bench_medium_test.go
@@ -60,3 +60,13 @@ func BenchmarkGoJayDecodeObjMedium(b *testing.B) {
}
}
}
+func BenchmarkGoJayUnsafeDecodeObjMedium(b *testing.B) {
+ b.ReportAllocs()
+ for i := 0; i < b.N; i++ {
+ result := benchmarks.MediumPayload{}
+ err := gojay.Unsafe.UnmarshalObject(benchmarks.MediumFixture, &result)
+ if err != nil {
+ b.Error(err)
+ }
+ }
+}
diff --git a/benchmarks/decoder/decoder_bench_small_test.go b/benchmarks/decoder/decoder_bench_small_test.go
@@ -59,3 +59,11 @@ func BenchmarkGoJayDecodeObjSmall(b *testing.B) {
gojay.UnmarshalObject(benchmarks.SmallFixture, &result)
}
}
+
+func BenchmarkGoJayUnsafeDecodeObjSmall(b *testing.B) {
+ b.ReportAllocs()
+ for i := 0; i < b.N; i++ {
+ result := benchmarks.SmallPayload{}
+ gojay.Unsafe.UnmarshalObject(benchmarks.SmallFixture, &result)
+ }
+}
diff --git a/decode.go b/decode.go
@@ -14,7 +14,8 @@ import (
// overflows the target type, UnmarshalArray skips that field and completes the unmarshaling as best it can.
func UnmarshalArray(data []byte, v UnmarshalerArray) error {
dec := newDecoder(nil, 0)
- dec.data = data
+ dec.data = make([]byte, len(data))
+ copy(dec.data, data)
dec.length = len(data)
_, err := dec.DecodeArray(v)
dec.addToPool()
@@ -35,7 +36,8 @@ func UnmarshalArray(data []byte, v UnmarshalerArray) error {
// overflows the target type, UnmarshalObject skips that field and completes the unmarshaling as best it can.
func UnmarshalObject(data []byte, v UnmarshalerObject) error {
dec := newDecoder(nil, 0)
- dec.data = data
+ dec.data = make([]byte, len(data))
+ copy(dec.data, data)
dec.length = len(data)
_, err := dec.DecodeObject(v)
dec.addToPool()
@@ -113,12 +115,14 @@ func Unmarshal(data []byte, v interface{}) error {
case UnmarshalerObject:
dec = newDecoder(nil, 0)
dec.length = len(data)
- dec.data = data
+ dec.data = make([]byte, len(data))
+ copy(dec.data, data)
_, err = dec.DecodeObject(vt)
case UnmarshalerArray:
dec = newDecoder(nil, 0)
dec.length = len(data)
- dec.data = data
+ dec.data = make([]byte, len(data))
+ copy(dec.data, data)
_, err = dec.DecodeArray(vt)
default:
return InvalidUnmarshalError(fmt.Sprintf(invalidUnmarshalErrorMsg, reflect.TypeOf(vt).String()))
diff --git a/decode_unsafe.go b/decode_unsafe.go
@@ -0,0 +1,108 @@
+package gojay
+
+import (
+ "fmt"
+ "reflect"
+)
+
+// 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) UnmarshalArray(data []byte, v UnmarshalerArray) error {
+ dec := newDecoder(nil, 0)
+ dec.data = data
+ dec.length = len(data)
+ _, err := dec.DecodeArray(v)
+ dec.addToPool()
+ if err != nil {
+ return err
+ }
+ if dec.err != nil {
+ return dec.err
+ }
+ return nil
+}
+
+func (u decUnsafe) UnmarshalObject(data []byte, v UnmarshalerObject) error {
+ dec := newDecoder(nil, 0)
+ dec.data = data
+ dec.length = len(data)
+ _, err := dec.DecodeObject(v)
+ dec.addToPool()
+ if err != nil {
+ return err
+ }
+ if dec.err != nil {
+ return dec.err
+ }
+ return nil
+}
+
+func (u decUnsafe) Unmarshal(data []byte, v interface{}) error {
+ var err error
+ var dec *Decoder
+ switch vt := v.(type) {
+ case *string:
+ dec = newDecoder(nil, 0)
+ dec.length = len(data)
+ dec.data = data
+ err = dec.DecodeString(vt)
+ case *int:
+ dec = newDecoder(nil, 0)
+ dec.length = len(data)
+ dec.data = data
+ err = dec.DecodeInt(vt)
+ case *int32:
+ dec = newDecoder(nil, 0)
+ dec.length = len(data)
+ dec.data = data
+ err = dec.DecodeInt32(vt)
+ case *uint32:
+ dec = newDecoder(nil, 0)
+ dec.length = len(data)
+ dec.data = data
+ err = dec.DecodeUint32(vt)
+ case *int64:
+ dec = newDecoder(nil, 0)
+ dec.length = len(data)
+ dec.data = data
+ err = dec.DecodeInt64(vt)
+ case *uint64:
+ dec = newDecoder(nil, 0)
+ dec.length = len(data)
+ dec.data = data
+ err = dec.DecodeUint64(vt)
+ case *float64:
+ dec = newDecoder(nil, 0)
+ dec.length = len(data)
+ dec.data = data
+ err = dec.DecodeFloat64(vt)
+ case *bool:
+ dec = newDecoder(nil, 0)
+ dec.length = len(data)
+ dec.data = data
+ err = dec.DecodeBool(vt)
+ case UnmarshalerObject:
+ dec = newDecoder(nil, 0)
+ dec.length = len(data)
+ dec.data = data
+ _, err = dec.DecodeObject(vt)
+ case UnmarshalerArray:
+ dec = newDecoder(nil, 0)
+ dec.length = len(data)
+ dec.data = data
+ _, err = dec.DecodeArray(vt)
+ default:
+ return InvalidUnmarshalError(fmt.Sprintf(invalidUnmarshalErrorMsg, reflect.TypeOf(vt).String()))
+ }
+ defer dec.addToPool()
+ if err != nil {
+ return err
+ }
+ return dec.err
+}