blob: 772a8f41701fd983bc8bfafd58af3992c47dc04f [file] [log] [blame]
Joe Tsai2fc306a2019-06-20 03:01:22 -07001// 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
5package proto
6
Joe Tsaice496b52020-01-02 15:47:15 -08007import (
8 "google.golang.org/protobuf/internal/pragma"
9 "google.golang.org/protobuf/reflect/protoreflect"
10)
11
12// MergeOptions configures the merger.
13//
14// Example usage:
15// MergeOptions{Shallow: true}.Merge(dst, src)
16type MergeOptions struct {
17 pragma.NoUnkeyedLiterals
18
19 // Shallow configures Merge to shallow copy messages, lists, and maps
20 // instead of allocating new ones in the destination if it does not already
21 // have one populated. Scalar bytes are copied by reference.
22 // If true, Merge must be given messages of the same concrete type.
23 //
24 // If false, Merge is guaranteed to produce deep copies of all mutable
25 // objects from the source into the destination. Since scalar bytes are
26 // mutable they are deep copied as a result.
27 //
28 // Invariant:
29 // var dst1, dst2 Message = ...
30 // Equal(dst1, dst2) // assume equal initially
31 // MergeOptions{Shallow: true}.Merge(dst1, src)
32 // MergeOptions{Shallow: false}.Merge(dst2, src)
33 // Equal(dst1, dst2) // equal regardless of whether Shallow is specified
34 Shallow bool
35}
36
Joe Tsaib7695fa2020-01-04 09:09:04 -080037// Clone returns a deep copy of m.
38// See MergeOptions.Clone for details.
39func Clone(m Message) Message {
40 return MergeOptions{}.Clone(m)
41}
42
Joe Tsaice496b52020-01-02 15:47:15 -080043// Merge merges src into dst, which must be messages with the same descriptor.
44// See MergeOptions.Merge for details.
45func Merge(dst, src Message) {
46 MergeOptions{}.Merge(dst, src)
47}
Joe Tsai2fc306a2019-06-20 03:01:22 -070048
Joe Tsaib7695fa2020-01-04 09:09:04 -080049// Clone returns a copy of m.
50// If Shallow is specified it makes a new message and shallow copies m into it.
51// If the top-level message is invalid, it returns an invalid message as well.
52func (o MergeOptions) Clone(m Message) Message {
53 // NOTE: Most usages of Clone assume the following properties:
54 // t := reflect.TypeOf(m)
55 // t == reflect.TypeOf(m.ProtoReflect().New().Interface())
56 // t == reflect.TypeOf(m.ProtoReflect().Type().Zero().Interface())
57 //
58 // Embedding protobuf messages breaks this since the parent type will have
59 // a forwarded ProtoReflect method, but the Interface method will return
60 // the underlying embedded message type.
61 return o.cloneMessage(m.ProtoReflect()).Interface()
62}
63
64func (o MergeOptions) cloneMessage(src protoreflect.Message) (dst protoreflect.Message) {
65 if !src.IsValid() {
66 return src.Type().Zero()
67 }
68 dst = src.New()
69 o.mergeMessage(dst, src)
70 return dst
71}
72
Joe Tsai2fc306a2019-06-20 03:01:22 -070073// Merge merges src into dst, which must be messages with the same descriptor.
74//
75// Populated scalar fields in src are copied to dst, while populated
76// singular messages in src are merged into dst by recursively calling Merge.
77// The elements of every list field in src is appended to the corresponded
78// list fields in dst. The entries of every map field in src is copied into
79// the corresponding map field in dst, possibly replacing existing entries.
80// The unknown fields of src are appended to the unknown fields of dst.
Joe Tsaice496b52020-01-02 15:47:15 -080081//
82// It is semantically equivalent to unmarshaling the encoded form of src
83// into dst with the UnmarshalOptions.Merge option specified.
84func (o MergeOptions) Merge(dst, src Message) {
85 dstMsg, srcMsg := dst.ProtoReflect(), src.ProtoReflect()
86 if o.Shallow {
87 if dstMsg.Type() != srcMsg.Type() {
88 panic("type mismatch")
89 }
90 } else {
91 if dstMsg.Descriptor() != srcMsg.Descriptor() {
92 panic("descriptor mismatch")
93 }
94 }
95 o.mergeMessage(dstMsg, srcMsg)
Joe Tsai2fc306a2019-06-20 03:01:22 -070096}
97
Joe Tsaice496b52020-01-02 15:47:15 -080098func (o MergeOptions) mergeMessage(dst, src protoreflect.Message) {
Joe Tsai2fc306a2019-06-20 03:01:22 -070099 src.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool {
100 switch {
101 case fd.IsList():
Joe Tsaice496b52020-01-02 15:47:15 -0800102 if o.Shallow && !dst.Has(fd) {
103 dst.Set(fd, v)
104 } else {
105 o.mergeList(dst.Mutable(fd).List(), v.List(), fd)
106 }
Joe Tsai2fc306a2019-06-20 03:01:22 -0700107 case fd.IsMap():
Joe Tsaice496b52020-01-02 15:47:15 -0800108 if o.Shallow && !dst.Has(fd) {
109 dst.Set(fd, v)
110 } else {
111 o.mergeMap(dst.Mutable(fd).Map(), v.Map(), fd.MapValue())
112 }
Joe Tsai2fc306a2019-06-20 03:01:22 -0700113 case fd.Message() != nil:
Joe Tsaice496b52020-01-02 15:47:15 -0800114 if o.Shallow && !dst.Has(fd) {
115 dst.Set(fd, v)
116 } else {
117 o.mergeMessage(dst.Mutable(fd).Message(), v.Message())
118 }
Joe Tsai2fc306a2019-06-20 03:01:22 -0700119 case fd.Kind() == protoreflect.BytesKind:
Joe Tsaice496b52020-01-02 15:47:15 -0800120 dst.Set(fd, o.cloneBytes(v))
Joe Tsai2fc306a2019-06-20 03:01:22 -0700121 default:
122 dst.Set(fd, v)
123 }
124 return true
125 })
126
Joe Tsaic9081442019-09-20 12:18:55 -0700127 if len(src.GetUnknown()) > 0 {
Joe Tsaice496b52020-01-02 15:47:15 -0800128 if o.Shallow && dst.GetUnknown() == nil {
129 dst.SetUnknown(src.GetUnknown())
130 } else {
131 dst.SetUnknown(append(dst.GetUnknown(), src.GetUnknown()...))
132 }
Joe Tsaic9081442019-09-20 12:18:55 -0700133 }
Joe Tsai2fc306a2019-06-20 03:01:22 -0700134}
135
Joe Tsaice496b52020-01-02 15:47:15 -0800136func (o MergeOptions) mergeList(dst, src protoreflect.List, fd protoreflect.FieldDescriptor) {
137 // Merge semantics appends to the end of the existing list.
Joe Tsai641611d2019-09-21 21:50:50 -0700138 for i, n := 0, src.Len(); i < n; i++ {
Joe Tsai2fc306a2019-06-20 03:01:22 -0700139 switch v := src.Get(i); {
140 case fd.Message() != nil:
Joe Tsaice496b52020-01-02 15:47:15 -0800141 if o.Shallow {
142 dst.Append(v)
143 } else {
144 dstv := dst.NewElement()
145 o.mergeMessage(dstv.Message(), v.Message())
146 dst.Append(dstv)
147 }
Joe Tsai2fc306a2019-06-20 03:01:22 -0700148 case fd.Kind() == protoreflect.BytesKind:
Joe Tsaice496b52020-01-02 15:47:15 -0800149 dst.Append(o.cloneBytes(v))
Joe Tsai2fc306a2019-06-20 03:01:22 -0700150 default:
151 dst.Append(v)
152 }
153 }
154}
155
Joe Tsaice496b52020-01-02 15:47:15 -0800156func (o MergeOptions) mergeMap(dst, src protoreflect.Map, fd protoreflect.FieldDescriptor) {
157 // Merge semantics replaces, rather than merges into existing entries.
Joe Tsai2fc306a2019-06-20 03:01:22 -0700158 src.Range(func(k protoreflect.MapKey, v protoreflect.Value) bool {
159 switch {
160 case fd.Message() != nil:
Joe Tsaice496b52020-01-02 15:47:15 -0800161 if o.Shallow {
162 dst.Set(k, v)
163 } else {
164 dstv := dst.NewValue()
165 o.mergeMessage(dstv.Message(), v.Message())
166 dst.Set(k, dstv)
167 }
Joe Tsai2fc306a2019-06-20 03:01:22 -0700168 case fd.Kind() == protoreflect.BytesKind:
Joe Tsaice496b52020-01-02 15:47:15 -0800169 dst.Set(k, o.cloneBytes(v))
Joe Tsai2fc306a2019-06-20 03:01:22 -0700170 default:
171 dst.Set(k, v)
172 }
173 return true
174 })
175}
176
Joe Tsaice496b52020-01-02 15:47:15 -0800177func (o MergeOptions) cloneBytes(v protoreflect.Value) protoreflect.Value {
178 if o.Shallow {
179 return v
180 }
Joe Tsai84177c92019-09-17 13:38:48 -0700181 return protoreflect.ValueOfBytes(append([]byte{}, v.Bytes()...))
Joe Tsai2fc306a2019-06-20 03:01:22 -0700182}