initialise
[debian/goprotobuf.git] / proto / clone.go
1 // Go support for Protocol Buffers - Google's data interchange format
2 //
3 // Copyright 2011 Google Inc.  All rights reserved.
4 // http://code.google.com/p/goprotobuf/
5 //
6 // Redistribution and use in source and binary forms, with or without
7 // modification, are permitted provided that the following conditions are
8 // met:
9 //
10 //     * Redistributions of source code must retain the above copyright
11 // notice, this list of conditions and the following disclaimer.
12 //     * Redistributions in binary form must reproduce the above
13 // copyright notice, this list of conditions and the following disclaimer
14 // in the documentation and/or other materials provided with the
15 // distribution.
16 //     * Neither the name of Google Inc. nor the names of its
17 // contributors may be used to endorse or promote products derived from
18 // this software without specific prior written permission.
19 //
20 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
32 // Protocol buffer deep copy.
33 // TODO: MessageSet and RawMessage.
34
35 package proto
36
37 import (
38         "log"
39         "reflect"
40         "strings"
41 )
42
43 // Clone returns a deep copy of a protocol buffer.
44 // pb must be a pointer to a protocol buffer struct.
45 func Clone(pb interface{}) interface{} {
46         in := reflect.ValueOf(pb)
47         if in.Kind() != reflect.Ptr || in.Elem().Kind() != reflect.Struct {
48                 return nil
49         }
50
51         out := reflect.New(in.Type().Elem())
52         copyStruct(out.Elem(), in.Elem())
53         return out.Interface()
54 }
55
56 func copyStruct(out, in reflect.Value) {
57         for i := 0; i < in.NumField(); i++ {
58                 f := in.Type().Field(i)
59                 if strings.HasPrefix(f.Name, "XXX_") {
60                         continue
61                 }
62                 copyAny(out.Field(i), in.Field(i))
63         }
64
65         if emIn, ok := in.Addr().Interface().(extendableProto); ok {
66                 emOut := out.Addr().Interface().(extendableProto)
67                 copyExtension(emOut.ExtensionMap(), emIn.ExtensionMap())
68         }
69
70         // TODO: Deal with XXX_unrecognized.
71 }
72
73 func copyAny(out, in reflect.Value) {
74         switch in.Kind() {
75         case reflect.Bool, reflect.Float32, reflect.Float64, reflect.Int32, reflect.Int64,
76                 reflect.String, reflect.Uint32, reflect.Uint64:
77                 out.Set(in)
78         case reflect.Ptr:
79                 if in.IsNil() {
80                         return
81                 }
82                 out.Set(reflect.New(in.Type().Elem()))
83                 copyAny(out.Elem(), in.Elem())
84         case reflect.Slice:
85                 if in.IsNil() {
86                         return
87                 }
88                 n := in.Len()
89                 out.Set(reflect.MakeSlice(in.Type(), n, n))
90                 switch in.Type().Elem().Kind() {
91                 case reflect.Bool, reflect.Float32, reflect.Float64, reflect.Int32, reflect.Int64,
92                         reflect.String, reflect.Uint32, reflect.Uint64, reflect.Uint8:
93                         reflect.Copy(out, in)
94                 default:
95                         for i := 0; i < n; i++ {
96                                 copyAny(out.Index(i), in.Index(i))
97                         }
98                 }
99         case reflect.Struct:
100                 copyStruct(out, in)
101         default:
102                 // unknown type, so not a protocol buffer
103                 log.Printf("proto: don't know how to copy %v", in)
104         }
105 }
106
107 func copyExtension(out, in map[int32]Extension) {
108         for extNum, eIn := range in {
109                 eOut := Extension{desc: eIn.desc}
110                 if eIn.value != nil {
111                         v := reflect.New(reflect.TypeOf(eIn.value)).Elem()
112                         copyAny(v, reflect.ValueOf(eIn.value))
113                         eOut.value = v.Interface()
114                 }
115                 if eIn.enc != nil {
116                         eOut.enc = make([]byte, len(eIn.enc))
117                         copy(eOut.enc, eIn.enc)
118                 }
119
120                 out[extNum] = eOut
121         }
122 }