Scott Baker | eee8dd8 | 2019-09-24 12:52:34 -0700 | [diff] [blame^] | 1 | // Go support for Protocol Buffers - Google's data interchange format |
| 2 | // |
| 3 | // Copyright 2011 The Go Authors. All rights reserved. |
| 4 | // https://github.com/golang/protobuf |
| 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 and merge. |
| 33 | // TODO: RawMessage. |
| 34 | |
| 35 | package proto |
| 36 | |
| 37 | import ( |
| 38 | "fmt" |
| 39 | "log" |
| 40 | "reflect" |
| 41 | "strings" |
| 42 | ) |
| 43 | |
| 44 | // Clone returns a deep copy of a protocol buffer. |
| 45 | func Clone(src Message) Message { |
| 46 | in := reflect.ValueOf(src) |
| 47 | if in.IsNil() { |
| 48 | return src |
| 49 | } |
| 50 | out := reflect.New(in.Type().Elem()) |
| 51 | dst := out.Interface().(Message) |
| 52 | Merge(dst, src) |
| 53 | return dst |
| 54 | } |
| 55 | |
| 56 | // Merger is the interface representing objects that can merge messages of the same type. |
| 57 | type Merger interface { |
| 58 | // Merge merges src into this message. |
| 59 | // Required and optional fields that are set in src will be set to that value in dst. |
| 60 | // Elements of repeated fields will be appended. |
| 61 | // |
| 62 | // Merge may panic if called with a different argument type than the receiver. |
| 63 | Merge(src Message) |
| 64 | } |
| 65 | |
| 66 | // generatedMerger is the custom merge method that generated protos will have. |
| 67 | // We must add this method since a generate Merge method will conflict with |
| 68 | // many existing protos that have a Merge data field already defined. |
| 69 | type generatedMerger interface { |
| 70 | XXX_Merge(src Message) |
| 71 | } |
| 72 | |
| 73 | // Merge merges src into dst. |
| 74 | // Required and optional fields that are set in src will be set to that value in dst. |
| 75 | // Elements of repeated fields will be appended. |
| 76 | // Merge panics if src and dst are not the same type, or if dst is nil. |
| 77 | func Merge(dst, src Message) { |
| 78 | if m, ok := dst.(Merger); ok { |
| 79 | m.Merge(src) |
| 80 | return |
| 81 | } |
| 82 | |
| 83 | in := reflect.ValueOf(src) |
| 84 | out := reflect.ValueOf(dst) |
| 85 | if out.IsNil() { |
| 86 | panic("proto: nil destination") |
| 87 | } |
| 88 | if in.Type() != out.Type() { |
| 89 | panic(fmt.Sprintf("proto.Merge(%T, %T) type mismatch", dst, src)) |
| 90 | } |
| 91 | if in.IsNil() { |
| 92 | return // Merge from nil src is a noop |
| 93 | } |
| 94 | if m, ok := dst.(generatedMerger); ok { |
| 95 | m.XXX_Merge(src) |
| 96 | return |
| 97 | } |
| 98 | mergeStruct(out.Elem(), in.Elem()) |
| 99 | } |
| 100 | |
| 101 | func mergeStruct(out, in reflect.Value) { |
| 102 | sprop := GetProperties(in.Type()) |
| 103 | for i := 0; i < in.NumField(); i++ { |
| 104 | f := in.Type().Field(i) |
| 105 | if strings.HasPrefix(f.Name, "XXX_") { |
| 106 | continue |
| 107 | } |
| 108 | mergeAny(out.Field(i), in.Field(i), false, sprop.Prop[i]) |
| 109 | } |
| 110 | |
| 111 | if emIn, err := extendable(in.Addr().Interface()); err == nil { |
| 112 | emOut, _ := extendable(out.Addr().Interface()) |
| 113 | mIn, muIn := emIn.extensionsRead() |
| 114 | if mIn != nil { |
| 115 | mOut := emOut.extensionsWrite() |
| 116 | muIn.Lock() |
| 117 | mergeExtension(mOut, mIn) |
| 118 | muIn.Unlock() |
| 119 | } |
| 120 | } |
| 121 | |
| 122 | uf := in.FieldByName("XXX_unrecognized") |
| 123 | if !uf.IsValid() { |
| 124 | return |
| 125 | } |
| 126 | uin := uf.Bytes() |
| 127 | if len(uin) > 0 { |
| 128 | out.FieldByName("XXX_unrecognized").SetBytes(append([]byte(nil), uin...)) |
| 129 | } |
| 130 | } |
| 131 | |
| 132 | // mergeAny performs a merge between two values of the same type. |
| 133 | // viaPtr indicates whether the values were indirected through a pointer (implying proto2). |
| 134 | // prop is set if this is a struct field (it may be nil). |
| 135 | func mergeAny(out, in reflect.Value, viaPtr bool, prop *Properties) { |
| 136 | if in.Type() == protoMessageType { |
| 137 | if !in.IsNil() { |
| 138 | if out.IsNil() { |
| 139 | out.Set(reflect.ValueOf(Clone(in.Interface().(Message)))) |
| 140 | } else { |
| 141 | Merge(out.Interface().(Message), in.Interface().(Message)) |
| 142 | } |
| 143 | } |
| 144 | return |
| 145 | } |
| 146 | switch in.Kind() { |
| 147 | case reflect.Bool, reflect.Float32, reflect.Float64, reflect.Int32, reflect.Int64, |
| 148 | reflect.String, reflect.Uint32, reflect.Uint64: |
| 149 | if !viaPtr && isProto3Zero(in) { |
| 150 | return |
| 151 | } |
| 152 | out.Set(in) |
| 153 | case reflect.Interface: |
| 154 | // Probably a oneof field; copy non-nil values. |
| 155 | if in.IsNil() { |
| 156 | return |
| 157 | } |
| 158 | // Allocate destination if it is not set, or set to a different type. |
| 159 | // Otherwise we will merge as normal. |
| 160 | if out.IsNil() || out.Elem().Type() != in.Elem().Type() { |
| 161 | out.Set(reflect.New(in.Elem().Elem().Type())) // interface -> *T -> T -> new(T) |
| 162 | } |
| 163 | mergeAny(out.Elem(), in.Elem(), false, nil) |
| 164 | case reflect.Map: |
| 165 | if in.Len() == 0 { |
| 166 | return |
| 167 | } |
| 168 | if out.IsNil() { |
| 169 | out.Set(reflect.MakeMap(in.Type())) |
| 170 | } |
| 171 | // For maps with value types of *T or []byte we need to deep copy each value. |
| 172 | elemKind := in.Type().Elem().Kind() |
| 173 | for _, key := range in.MapKeys() { |
| 174 | var val reflect.Value |
| 175 | switch elemKind { |
| 176 | case reflect.Ptr: |
| 177 | val = reflect.New(in.Type().Elem().Elem()) |
| 178 | mergeAny(val, in.MapIndex(key), false, nil) |
| 179 | case reflect.Slice: |
| 180 | val = in.MapIndex(key) |
| 181 | val = reflect.ValueOf(append([]byte{}, val.Bytes()...)) |
| 182 | default: |
| 183 | val = in.MapIndex(key) |
| 184 | } |
| 185 | out.SetMapIndex(key, val) |
| 186 | } |
| 187 | case reflect.Ptr: |
| 188 | if in.IsNil() { |
| 189 | return |
| 190 | } |
| 191 | if out.IsNil() { |
| 192 | out.Set(reflect.New(in.Elem().Type())) |
| 193 | } |
| 194 | mergeAny(out.Elem(), in.Elem(), true, nil) |
| 195 | case reflect.Slice: |
| 196 | if in.IsNil() { |
| 197 | return |
| 198 | } |
| 199 | if in.Type().Elem().Kind() == reflect.Uint8 { |
| 200 | // []byte is a scalar bytes field, not a repeated field. |
| 201 | |
| 202 | // Edge case: if this is in a proto3 message, a zero length |
| 203 | // bytes field is considered the zero value, and should not |
| 204 | // be merged. |
| 205 | if prop != nil && prop.proto3 && in.Len() == 0 { |
| 206 | return |
| 207 | } |
| 208 | |
| 209 | // Make a deep copy. |
| 210 | // Append to []byte{} instead of []byte(nil) so that we never end up |
| 211 | // with a nil result. |
| 212 | out.SetBytes(append([]byte{}, in.Bytes()...)) |
| 213 | return |
| 214 | } |
| 215 | n := in.Len() |
| 216 | if out.IsNil() { |
| 217 | out.Set(reflect.MakeSlice(in.Type(), 0, n)) |
| 218 | } |
| 219 | switch in.Type().Elem().Kind() { |
| 220 | case reflect.Bool, reflect.Float32, reflect.Float64, reflect.Int32, reflect.Int64, |
| 221 | reflect.String, reflect.Uint32, reflect.Uint64: |
| 222 | out.Set(reflect.AppendSlice(out, in)) |
| 223 | default: |
| 224 | for i := 0; i < n; i++ { |
| 225 | x := reflect.Indirect(reflect.New(in.Type().Elem())) |
| 226 | mergeAny(x, in.Index(i), false, nil) |
| 227 | out.Set(reflect.Append(out, x)) |
| 228 | } |
| 229 | } |
| 230 | case reflect.Struct: |
| 231 | mergeStruct(out, in) |
| 232 | default: |
| 233 | // unknown type, so not a protocol buffer |
| 234 | log.Printf("proto: don't know how to copy %v", in) |
| 235 | } |
| 236 | } |
| 237 | |
| 238 | func mergeExtension(out, in map[int32]Extension) { |
| 239 | for extNum, eIn := range in { |
| 240 | eOut := Extension{desc: eIn.desc} |
| 241 | if eIn.value != nil { |
| 242 | v := reflect.New(reflect.TypeOf(eIn.value)).Elem() |
| 243 | mergeAny(v, reflect.ValueOf(eIn.value), false, nil) |
| 244 | eOut.value = v.Interface() |
| 245 | } |
| 246 | if eIn.enc != nil { |
| 247 | eOut.enc = make([]byte, len(eIn.enc)) |
| 248 | copy(eOut.enc, eIn.enc) |
| 249 | } |
| 250 | |
| 251 | out[extNum] = eOut |
| 252 | } |
| 253 | } |