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 | }
|
---|