| David Symonds | e6a88c3 | 2011-09-13 13:34:46 +1000 | [diff] [blame] | 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. |
| David Symonds | 9f60f43 | 2012-06-14 09:45:25 +1000 | [diff] [blame] | 44 | func Clone(pb Message) Message { |
| David Symonds | e6a88c3 | 2011-09-13 13:34:46 +1000 | [diff] [blame] | 45 | in := reflect.ValueOf(pb) |
| David Symonds | 10c93ba | 2012-08-04 16:38:08 +1000 | [diff] [blame^] | 46 | if in.IsNil() { |
| 47 | return pb |
| David Symonds | e6a88c3 | 2011-09-13 13:34:46 +1000 | [diff] [blame] | 48 | } |
| 49 | |
| 50 | out := reflect.New(in.Type().Elem()) |
| 51 | copyStruct(out.Elem(), in.Elem()) |
| David Symonds | 9f60f43 | 2012-06-14 09:45:25 +1000 | [diff] [blame] | 52 | return out.Interface().(Message) |
| David Symonds | e6a88c3 | 2011-09-13 13:34:46 +1000 | [diff] [blame] | 53 | } |
| 54 | |
| 55 | func 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 Symonds | 6342980 | 2011-09-22 08:23:55 +1000 | [diff] [blame] | 64 | if emIn, ok := in.Addr().Interface().(extendableProto); ok { |
| 65 | emOut := out.Addr().Interface().(extendableProto) |
| 66 | copyExtension(emOut.ExtensionMap(), emIn.ExtensionMap()) |
| David Symonds | e6a88c3 | 2011-09-13 13:34:46 +1000 | [diff] [blame] | 67 | } |
| 68 | |
| David Symonds | 10c93ba | 2012-08-04 16:38:08 +1000 | [diff] [blame^] | 69 | uin := in.FieldByName("XXX_unrecognized").Bytes() |
| 70 | if len(uin) > 0 { |
| 71 | out.FieldByName("XXX_unrecognized").SetBytes(append([]byte(nil), uin...)) |
| 72 | } |
| David Symonds | e6a88c3 | 2011-09-13 13:34:46 +1000 | [diff] [blame] | 73 | } |
| 74 | |
| 75 | func copyAny(out, in reflect.Value) { |
| David Symonds | 007ed9d | 2012-07-24 10:59:36 +1000 | [diff] [blame] | 76 | if in.Type() == protoMessageType { |
| David Symonds | 10c93ba | 2012-08-04 16:38:08 +1000 | [diff] [blame^] | 77 | if !in.IsNil() { |
| 78 | out.Set(reflect.ValueOf(Clone(in.Interface().(Message)))) |
| 79 | } |
| David Symonds | 007ed9d | 2012-07-24 10:59:36 +1000 | [diff] [blame] | 80 | return |
| 81 | } |
| David Symonds | e6a88c3 | 2011-09-13 13:34:46 +1000 | [diff] [blame] | 82 | 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 | |
| 115 | func copyExtension(out, in map[int32]Extension) { |
| 116 | for extNum, eIn := range in { |
| 117 | eOut := Extension{desc: eIn.desc} |
| 118 | if eIn.value != nil { |
| David Symonds | 6342980 | 2011-09-22 08:23:55 +1000 | [diff] [blame] | 119 | v := reflect.New(reflect.TypeOf(eIn.value)).Elem() |
| 120 | copyAny(v, reflect.ValueOf(eIn.value)) |
| 121 | eOut.value = v.Interface() |
| David Symonds | e6a88c3 | 2011-09-13 13:34:46 +1000 | [diff] [blame] | 122 | } |
| 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 | } |