merge.go (4485B)
1 // Copyright 2019 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package proto 6 7 import ( 8 "fmt" 9 10 "google.golang.org/protobuf/reflect/protoreflect" 11 "google.golang.org/protobuf/runtime/protoiface" 12 ) 13 14 // Merge merges src into dst, which must be a message with the same descriptor. 15 // 16 // Populated scalar fields in src are copied to dst, while populated 17 // singular messages in src are merged into dst by recursively calling Merge. 18 // The elements of every list field in src is appended to the corresponded 19 // list fields in dst. The entries of every map field in src is copied into 20 // the corresponding map field in dst, possibly replacing existing entries. 21 // The unknown fields of src are appended to the unknown fields of dst. 22 // 23 // It is semantically equivalent to unmarshaling the encoded form of src 24 // into dst with the UnmarshalOptions.Merge option specified. 25 func Merge(dst, src Message) { 26 // TODO: Should nil src be treated as semantically equivalent to a 27 // untyped, read-only, empty message? What about a nil dst? 28 29 dstMsg, srcMsg := dst.ProtoReflect(), src.ProtoReflect() 30 if dstMsg.Descriptor() != srcMsg.Descriptor() { 31 if got, want := dstMsg.Descriptor().FullName(), srcMsg.Descriptor().FullName(); got != want { 32 panic(fmt.Sprintf("descriptor mismatch: %v != %v", got, want)) 33 } 34 panic("descriptor mismatch") 35 } 36 mergeOptions{}.mergeMessage(dstMsg, srcMsg) 37 } 38 39 // Clone returns a deep copy of m. 40 // If the top-level message is invalid, it returns an invalid message as well. 41 func Clone(m Message) Message { 42 // NOTE: Most usages of Clone assume the following properties: 43 // t := reflect.TypeOf(m) 44 // t == reflect.TypeOf(m.ProtoReflect().New().Interface()) 45 // t == reflect.TypeOf(m.ProtoReflect().Type().Zero().Interface()) 46 // 47 // Embedding protobuf messages breaks this since the parent type will have 48 // a forwarded ProtoReflect method, but the Interface method will return 49 // the underlying embedded message type. 50 if m == nil { 51 return nil 52 } 53 src := m.ProtoReflect() 54 if !src.IsValid() { 55 return src.Type().Zero().Interface() 56 } 57 dst := src.New() 58 mergeOptions{}.mergeMessage(dst, src) 59 return dst.Interface() 60 } 61 62 // mergeOptions provides a namespace for merge functions, and can be 63 // exported in the future if we add user-visible merge options. 64 type mergeOptions struct{} 65 66 func (o mergeOptions) mergeMessage(dst, src protoreflect.Message) { 67 methods := protoMethods(dst) 68 if methods != nil && methods.Merge != nil { 69 in := protoiface.MergeInput{ 70 Destination: dst, 71 Source: src, 72 } 73 out := methods.Merge(in) 74 if out.Flags&protoiface.MergeComplete != 0 { 75 return 76 } 77 } 78 79 if !dst.IsValid() { 80 panic(fmt.Sprintf("cannot merge into invalid %v message", dst.Descriptor().FullName())) 81 } 82 83 src.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool { 84 switch { 85 case fd.IsList(): 86 o.mergeList(dst.Mutable(fd).List(), v.List(), fd) 87 case fd.IsMap(): 88 o.mergeMap(dst.Mutable(fd).Map(), v.Map(), fd.MapValue()) 89 case fd.Message() != nil: 90 o.mergeMessage(dst.Mutable(fd).Message(), v.Message()) 91 case fd.Kind() == protoreflect.BytesKind: 92 dst.Set(fd, o.cloneBytes(v)) 93 default: 94 dst.Set(fd, v) 95 } 96 return true 97 }) 98 99 if len(src.GetUnknown()) > 0 { 100 dst.SetUnknown(append(dst.GetUnknown(), src.GetUnknown()...)) 101 } 102 } 103 104 func (o mergeOptions) mergeList(dst, src protoreflect.List, fd protoreflect.FieldDescriptor) { 105 // Merge semantics appends to the end of the existing list. 106 for i, n := 0, src.Len(); i < n; i++ { 107 switch v := src.Get(i); { 108 case fd.Message() != nil: 109 dstv := dst.NewElement() 110 o.mergeMessage(dstv.Message(), v.Message()) 111 dst.Append(dstv) 112 case fd.Kind() == protoreflect.BytesKind: 113 dst.Append(o.cloneBytes(v)) 114 default: 115 dst.Append(v) 116 } 117 } 118 } 119 120 func (o mergeOptions) mergeMap(dst, src protoreflect.Map, fd protoreflect.FieldDescriptor) { 121 // Merge semantics replaces, rather than merges into existing entries. 122 src.Range(func(k protoreflect.MapKey, v protoreflect.Value) bool { 123 switch { 124 case fd.Message() != nil: 125 dstv := dst.NewValue() 126 o.mergeMessage(dstv.Message(), v.Message()) 127 dst.Set(k, dstv) 128 case fd.Kind() == protoreflect.BytesKind: 129 dst.Set(k, o.cloneBytes(v)) 130 default: 131 dst.Set(k, v) 132 } 133 return true 134 }) 135 } 136 137 func (o mergeOptions) cloneBytes(v protoreflect.Value) protoreflect.Value { 138 return protoreflect.ValueOfBytes(append([]byte{}, v.Bytes()...)) 139 }