Commit 4d031abc authored by Kris Rousey's avatar Kris Rousey

Changing the implementation of DeepCopy to use reflection instead of Gob encode/decode.

parent 3481db8a
/*
Copyright 2015 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package api_test
import (
"math/rand"
"reflect"
"testing"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/testapi"
apitesting "github.com/GoogleCloudPlatform/kubernetes/pkg/api/testing"
"github.com/GoogleCloudPlatform/kubernetes/pkg/conversion"
)
func TestDeepCopyApiObjects(t *testing.T) {
for i := 0; i < *fuzzIters; i++ {
for _, version := range []string{"", testapi.Version()} {
f := apitesting.FuzzerFor(t, version, rand.NewSource(rand.Int63()))
for kind := range api.Scheme.KnownTypes(version) {
item, err := api.Scheme.New(version, kind)
if err != nil {
t.Fatalf("Could not create a %s: %s", kind, err)
}
f.Fuzz(item)
itemCopy, err := conversion.DeepCopy(item)
if err != nil {
t.Errorf("Could not deep copy a %s: %s", kind, err)
continue
}
if !reflect.DeepEqual(item, itemCopy) {
t.Errorf("expected %#v\ngot %#v", item, itemCopy)
}
}
}
}
}
......@@ -17,52 +17,98 @@ limitations under the License.
package conversion
import (
"bytes"
"encoding/gob"
"fmt"
"reflect"
"sync"
)
// pool is a pool of copiers
var pool = sync.Pool{
New: func() interface{} { return newGobCopier() },
}
// DeepCopy makes a deep copy of source or returns an error.
func DeepCopy(source interface{}) (interface{}, error) {
v := reflect.New(reflect.TypeOf(source))
v, err := deepCopy(reflect.ValueOf(source))
return v.Interface(), err
}
c := pool.Get().(gobCopier)
defer pool.Put(c)
if err := c.CopyInto(v.Interface(), source); err != nil {
return nil, err
func deepCopy(src reflect.Value) (reflect.Value, error) {
switch src.Kind() {
case reflect.Chan, reflect.Func, reflect.UnsafePointer, reflect.Uintptr:
return src, fmt.Errorf("cannot deep copy kind: %s", src.Kind())
case reflect.Array:
dst := reflect.New(src.Type())
for i := 0; i < src.Len(); i++ {
copyVal, err := deepCopy(src.Index(i))
if err != nil {
return src, err
}
dst.Elem().Index(i).Set(copyVal)
}
return dst.Elem(), nil
return v.Elem().Interface(), nil
}
case reflect.Interface:
if src.IsNil() {
return src, nil
}
return deepCopy(src.Elem())
// gobCopier provides a copy mechanism for objects using Gob.
// This object is not safe for multiple threads because buffer
// is shared.
type gobCopier struct {
enc *gob.Encoder
dec *gob.Decoder
}
case reflect.Map:
if src.IsNil() {
return src, nil
}
dst := reflect.MakeMap(src.Type())
for _, k := range src.MapKeys() {
copyVal, err := deepCopy(src.MapIndex(k))
if err != nil {
return src, err
}
dst.SetMapIndex(k, copyVal)
}
return dst, nil
func newGobCopier() gobCopier {
buf := &bytes.Buffer{}
return gobCopier{
enc: gob.NewEncoder(buf),
dec: gob.NewDecoder(buf),
case reflect.Ptr:
if src.IsNil() {
return src, nil
}
}
dst := reflect.New(src.Type().Elem())
copyVal, err := deepCopy(src.Elem())
if err != nil {
return src, err
}
dst.Elem().Set(copyVal)
return dst, nil
case reflect.Slice:
if src.IsNil() {
return src, nil
}
dst := reflect.MakeSlice(src.Type(), 0, src.Len())
for i := 0; i < src.Len(); i++ {
copyVal, err := deepCopy(src.Index(i))
if err != nil {
return src, err
}
dst = reflect.Append(dst, copyVal)
}
return dst, nil
func (c *gobCopier) CopyInto(dst, src interface{}) error {
if err := c.enc.Encode(src); err != nil {
return err
case reflect.Struct:
dst := reflect.New(src.Type())
for i := 0; i < src.NumField(); i++ {
if !dst.Elem().Field(i).CanSet() {
// Can't set private fields. At this point, the
// best we can do is a shallow copy. For
// example, time.Time is a value type with
// private members that can be shallow copied.
return src, nil
}
copyVal, err := deepCopy(src.Field(i))
if err != nil {
return src, err
}
if err := c.dec.Decode(dst); err != nil {
return err
dst.Elem().Field(i).Set(copyVal)
}
return dst.Elem(), nil
default:
// Value types like numbers, booleans, and strings.
return src, nil
}
return nil
}
......@@ -108,6 +108,24 @@ func TestDeepCopyPointerSeparate(t *testing.T) {
}
}
func TestDeepCopyStruct(t *testing.T) {
type Foo struct {
A int
}
type Bar struct {
Foo
F *Foo
}
a := &Bar{Foo{1}, &Foo{2}}
b := copyOrDie(t, a).(*Bar)
a.A = 3
a.F.A = 4
if b.A != 1 || b.F.A != 2 {
t.Errorf("deep copy wasn't deep: %#v, %#v", a, b)
}
}
var result interface{}
func BenchmarkDeepCopy(b *testing.B) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment