blob: fbd9c73f98cfe018c1a49b98427cb7b4df1906f3 [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
37// Merge merges src into dst, which must be messages with the same descriptor.
38// See MergeOptions.Merge for details.
39func Merge(dst, src Message) {
40 MergeOptions{}.Merge(dst, src)
41}
Joe Tsai2fc306a2019-06-20 03:01:22 -070042
43// Merge merges src into dst, which must be messages with the same descriptor.
44//
45// Populated scalar fields in src are copied to dst, while populated
46// singular messages in src are merged into dst by recursively calling Merge.
47// The elements of every list field in src is appended to the corresponded
48// list fields in dst. The entries of every map field in src is copied into
49// the corresponding map field in dst, possibly replacing existing entries.
50// The unknown fields of src are appended to the unknown fields of dst.
Joe Tsaice496b52020-01-02 15:47:15 -080051//
52// It is semantically equivalent to unmarshaling the encoded form of src
53// into dst with the UnmarshalOptions.Merge option specified.
54func (o MergeOptions) Merge(dst, src Message) {
55 dstMsg, srcMsg := dst.ProtoReflect(), src.ProtoReflect()
56 if o.Shallow {
57 if dstMsg.Type() != srcMsg.Type() {
58 panic("type mismatch")
59 }
60 } else {
61 if dstMsg.Descriptor() != srcMsg.Descriptor() {
62 panic("descriptor mismatch")
63 }
64 }
65 o.mergeMessage(dstMsg, srcMsg)
Joe Tsai2fc306a2019-06-20 03:01:22 -070066}
67
Joe Tsaice496b52020-01-02 15:47:15 -080068func (o MergeOptions) mergeMessage(dst, src protoreflect.Message) {
Joe Tsai2fc306a2019-06-20 03:01:22 -070069 src.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool {
70 switch {
71 case fd.IsList():
Joe Tsaice496b52020-01-02 15:47:15 -080072 if o.Shallow && !dst.Has(fd) {
73 dst.Set(fd, v)
74 } else {
75 o.mergeList(dst.Mutable(fd).List(), v.List(), fd)
76 }
Joe Tsai2fc306a2019-06-20 03:01:22 -070077 case fd.IsMap():
Joe Tsaice496b52020-01-02 15:47:15 -080078 if o.Shallow && !dst.Has(fd) {
79 dst.Set(fd, v)
80 } else {
81 o.mergeMap(dst.Mutable(fd).Map(), v.Map(), fd.MapValue())
82 }
Joe Tsai2fc306a2019-06-20 03:01:22 -070083 case fd.Message() != nil:
Joe Tsaice496b52020-01-02 15:47:15 -080084 if o.Shallow && !dst.Has(fd) {
85 dst.Set(fd, v)
86 } else {
87 o.mergeMessage(dst.Mutable(fd).Message(), v.Message())
88 }
Joe Tsai2fc306a2019-06-20 03:01:22 -070089 case fd.Kind() == protoreflect.BytesKind:
Joe Tsaice496b52020-01-02 15:47:15 -080090 dst.Set(fd, o.cloneBytes(v))
Joe Tsai2fc306a2019-06-20 03:01:22 -070091 default:
92 dst.Set(fd, v)
93 }
94 return true
95 })
96
Joe Tsaic9081442019-09-20 12:18:55 -070097 if len(src.GetUnknown()) > 0 {
Joe Tsaice496b52020-01-02 15:47:15 -080098 if o.Shallow && dst.GetUnknown() == nil {
99 dst.SetUnknown(src.GetUnknown())
100 } else {
101 dst.SetUnknown(append(dst.GetUnknown(), src.GetUnknown()...))
102 }
Joe Tsaic9081442019-09-20 12:18:55 -0700103 }
Joe Tsai2fc306a2019-06-20 03:01:22 -0700104}
105
Joe Tsaice496b52020-01-02 15:47:15 -0800106func (o MergeOptions) mergeList(dst, src protoreflect.List, fd protoreflect.FieldDescriptor) {
107 // Merge semantics appends to the end of the existing list.
Joe Tsai641611d2019-09-21 21:50:50 -0700108 for i, n := 0, src.Len(); i < n; i++ {
Joe Tsai2fc306a2019-06-20 03:01:22 -0700109 switch v := src.Get(i); {
110 case fd.Message() != nil:
Joe Tsaice496b52020-01-02 15:47:15 -0800111 if o.Shallow {
112 dst.Append(v)
113 } else {
114 dstv := dst.NewElement()
115 o.mergeMessage(dstv.Message(), v.Message())
116 dst.Append(dstv)
117 }
Joe Tsai2fc306a2019-06-20 03:01:22 -0700118 case fd.Kind() == protoreflect.BytesKind:
Joe Tsaice496b52020-01-02 15:47:15 -0800119 dst.Append(o.cloneBytes(v))
Joe Tsai2fc306a2019-06-20 03:01:22 -0700120 default:
121 dst.Append(v)
122 }
123 }
124}
125
Joe Tsaice496b52020-01-02 15:47:15 -0800126func (o MergeOptions) mergeMap(dst, src protoreflect.Map, fd protoreflect.FieldDescriptor) {
127 // Merge semantics replaces, rather than merges into existing entries.
Joe Tsai2fc306a2019-06-20 03:01:22 -0700128 src.Range(func(k protoreflect.MapKey, v protoreflect.Value) bool {
129 switch {
130 case fd.Message() != nil:
Joe Tsaice496b52020-01-02 15:47:15 -0800131 if o.Shallow {
132 dst.Set(k, v)
133 } else {
134 dstv := dst.NewValue()
135 o.mergeMessage(dstv.Message(), v.Message())
136 dst.Set(k, dstv)
137 }
Joe Tsai2fc306a2019-06-20 03:01:22 -0700138 case fd.Kind() == protoreflect.BytesKind:
Joe Tsaice496b52020-01-02 15:47:15 -0800139 dst.Set(k, o.cloneBytes(v))
Joe Tsai2fc306a2019-06-20 03:01:22 -0700140 default:
141 dst.Set(k, v)
142 }
143 return true
144 })
145}
146
Joe Tsaice496b52020-01-02 15:47:15 -0800147func (o MergeOptions) cloneBytes(v protoreflect.Value) protoreflect.Value {
148 if o.Shallow {
149 return v
150 }
Joe Tsai84177c92019-09-17 13:38:48 -0700151 return protoreflect.ValueOfBytes(append([]byte{}, v.Bytes()...))
Joe Tsai2fc306a2019-06-20 03:01:22 -0700152}