blob: b23f46b5dfbb51a523987323250d62377baf4799 [file] [log] [blame]
David Symondse6a88c32011-09-13 13:34:46 +10001// 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
35package proto
36
37import (
38 "log"
39 "reflect"
40 "strings"
41)
42
43// Clone returns a deep copy of a protocol buffer.
David Symonds9f60f432012-06-14 09:45:25 +100044func Clone(pb Message) Message {
David Symondse6a88c32011-09-13 13:34:46 +100045 in := reflect.ValueOf(pb)
David Symonds10c93ba2012-08-04 16:38:08 +100046 if in.IsNil() {
47 return pb
David Symondse6a88c32011-09-13 13:34:46 +100048 }
49
50 out := reflect.New(in.Type().Elem())
51 copyStruct(out.Elem(), in.Elem())
David Symonds9f60f432012-06-14 09:45:25 +100052 return out.Interface().(Message)
David Symondse6a88c32011-09-13 13:34:46 +100053}
54
55func copyStruct(out, in reflect.Value) {
56 for i := 0; i < in.NumField(); i++ {
57 f := in.Type().Field(i)
58 if strings.HasPrefix(f.Name, "XXX_") {
59 continue
60 }
61 copyAny(out.Field(i), in.Field(i))
62 }
63
David Symonds63429802011-09-22 08:23:55 +100064 if emIn, ok := in.Addr().Interface().(extendableProto); ok {
65 emOut := out.Addr().Interface().(extendableProto)
66 copyExtension(emOut.ExtensionMap(), emIn.ExtensionMap())
David Symondse6a88c32011-09-13 13:34:46 +100067 }
68
David Symonds10c93ba2012-08-04 16:38:08 +100069 uin := in.FieldByName("XXX_unrecognized").Bytes()
70 if len(uin) > 0 {
71 out.FieldByName("XXX_unrecognized").SetBytes(append([]byte(nil), uin...))
72 }
David Symondse6a88c32011-09-13 13:34:46 +100073}
74
75func copyAny(out, in reflect.Value) {
David Symonds007ed9d2012-07-24 10:59:36 +100076 if in.Type() == protoMessageType {
David Symonds10c93ba2012-08-04 16:38:08 +100077 if !in.IsNil() {
78 out.Set(reflect.ValueOf(Clone(in.Interface().(Message))))
79 }
David Symonds007ed9d2012-07-24 10:59:36 +100080 return
81 }
David Symondse6a88c32011-09-13 13:34:46 +100082 switch in.Kind() {
83 case reflect.Bool, reflect.Float32, reflect.Float64, reflect.Int32, reflect.Int64,
84 reflect.String, reflect.Uint32, reflect.Uint64:
85 out.Set(in)
86 case reflect.Ptr:
87 if in.IsNil() {
88 return
89 }
90 out.Set(reflect.New(in.Type().Elem()))
91 copyAny(out.Elem(), in.Elem())
92 case reflect.Slice:
93 if in.IsNil() {
94 return
95 }
96 n := in.Len()
97 out.Set(reflect.MakeSlice(in.Type(), n, n))
98 switch in.Type().Elem().Kind() {
99 case reflect.Bool, reflect.Float32, reflect.Float64, reflect.Int32, reflect.Int64,
100 reflect.String, reflect.Uint32, reflect.Uint64, reflect.Uint8:
101 reflect.Copy(out, in)
102 default:
103 for i := 0; i < n; i++ {
104 copyAny(out.Index(i), in.Index(i))
105 }
106 }
107 case reflect.Struct:
108 copyStruct(out, in)
109 default:
110 // unknown type, so not a protocol buffer
111 log.Printf("proto: don't know how to copy %v", in)
112 }
113}
114
115func copyExtension(out, in map[int32]Extension) {
116 for extNum, eIn := range in {
117 eOut := Extension{desc: eIn.desc}
118 if eIn.value != nil {
David Symonds63429802011-09-22 08:23:55 +1000119 v := reflect.New(reflect.TypeOf(eIn.value)).Elem()
120 copyAny(v, reflect.ValueOf(eIn.value))
121 eOut.value = v.Interface()
David Symondse6a88c32011-09-13 13:34:46 +1000122 }
123 if eIn.enc != nil {
124 eOut.enc = make([]byte, len(eIn.enc))
125 copy(eOut.enc, eIn.enc)
126 }
127
128 out[extNum] = eOut
129 }
130}