unmarshal: replace manual array element copy with reflect.Copy
This commit also adds a benchmark for array unmarshaling. The difference
is staggering.
name old time/op new time/op delta
LargeArrayUnmarshal-4 23.4µs ± 2% 0.4µs ± 2% -98.49% (p=0.000 n=9+9)
name old alloc/op new alloc/op delta
LargeArrayUnmarshal-4 96.0B ± 0% 128.0B ± 0% +33.33% (p=0.000 n=10+10)
name old allocs/op new allocs/op delta
LargeArrayUnmarshal-4 3.00 ± 0% 4.00 ± 0% +33.33% (p=0.000 n=10+10)
diff --git a/unmarshal.go b/unmarshal.go
index 342243e..63b4b1d 100644
--- a/unmarshal.go
+++ b/unmarshal.go
@@ -188,11 +188,8 @@
if val.Len() < len(b) {
panic(fmt.Errorf("plist: attempted to unmarshal %d bytes into a byte array of size %d", len(b), val.Len()))
}
-
- // slow path -- arrays don't support .SetBytes
- for i, v := range b {
- val.Index(i).Set(reflect.ValueOf(v))
- }
+ sval := reflect.ValueOf(b)
+ reflect.Copy(val, sval)
}
case cfUID:
if val.Type() == uidType {
diff --git a/unmarshal_test.go b/unmarshal_test.go
index eaf3cdb..c673793 100644
--- a/unmarshal_test.go
+++ b/unmarshal_test.go
@@ -30,3 +30,13 @@
d.unmarshal(plistValueTree, reflect.ValueOf(&xval))
}
}
+
+func BenchmarkLargeArrayUnmarshal(b *testing.B) {
+ var xval [1024]byte
+ pval := cfData(make([]byte, 1024))
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ d := &Decoder{}
+ d.unmarshal(pval, reflect.ValueOf(&xval))
+ }
+}