blob: f7af9980d72878edec5f04d13f92c7b5962c48fc [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
7import "google.golang.org/protobuf/reflect/protoreflect"
8
9// Merge merges src into dst, which must be messages with the same descriptor.
10//
11// Populated scalar fields in src are copied to dst, while populated
12// singular messages in src are merged into dst by recursively calling Merge.
13// The elements of every list field in src is appended to the corresponded
14// list fields in dst. The entries of every map field in src is copied into
15// the corresponding map field in dst, possibly replacing existing entries.
16// The unknown fields of src are appended to the unknown fields of dst.
17func Merge(dst, src Message) {
18 mergeMessage(dst.ProtoReflect(), src.ProtoReflect())
19}
20
21func mergeMessage(dst, src protoreflect.Message) {
22 if dst.Descriptor() != src.Descriptor() {
23 panic("descriptor mismatch")
24 }
25
26 src.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool {
27 switch {
28 case fd.IsList():
29 mergeList(dst.Mutable(fd).List(), v.List(), fd)
30 case fd.IsMap():
31 mergeMap(dst.Mutable(fd).Map(), v.Map(), fd.MapValue())
32 case fd.Message() != nil:
33 mergeMessage(dst.Mutable(fd).Message(), v.Message())
34 case fd.Kind() == protoreflect.BytesKind:
35 dst.Set(fd, cloneBytes(v))
36 default:
37 dst.Set(fd, v)
38 }
39 return true
40 })
41
Joe Tsaic9081442019-09-20 12:18:55 -070042 if len(src.GetUnknown()) > 0 {
43 dst.SetUnknown(append(dst.GetUnknown(), src.GetUnknown()...))
44 }
Joe Tsai2fc306a2019-06-20 03:01:22 -070045}
46
47func mergeList(dst, src protoreflect.List, fd protoreflect.FieldDescriptor) {
Joe Tsai641611d2019-09-21 21:50:50 -070048 for i, n := 0, src.Len(); i < n; i++ {
Joe Tsai2fc306a2019-06-20 03:01:22 -070049 switch v := src.Get(i); {
50 case fd.Message() != nil:
Damien Neild91c4222019-09-04 10:46:00 -070051 dstv := dst.NewElement()
52 mergeMessage(dstv.Message(), v.Message())
53 dst.Append(dstv)
Joe Tsai2fc306a2019-06-20 03:01:22 -070054 case fd.Kind() == protoreflect.BytesKind:
55 dst.Append(cloneBytes(v))
56 default:
57 dst.Append(v)
58 }
59 }
60}
61
62func mergeMap(dst, src protoreflect.Map, fd protoreflect.FieldDescriptor) {
63 src.Range(func(k protoreflect.MapKey, v protoreflect.Value) bool {
64 switch {
65 case fd.Message() != nil:
Damien Neild91c4222019-09-04 10:46:00 -070066 dstv := dst.NewValue()
67 mergeMessage(dstv.Message(), v.Message())
68 dst.Set(k, dstv) // may replace existing entry
Joe Tsai2fc306a2019-06-20 03:01:22 -070069 case fd.Kind() == protoreflect.BytesKind:
70 dst.Set(k, cloneBytes(v))
71 default:
72 dst.Set(k, v)
73 }
74 return true
75 })
76}
77
78func cloneBytes(v protoreflect.Value) protoreflect.Value {
Joe Tsai84177c92019-09-17 13:38:48 -070079 return protoreflect.ValueOfBytes(append([]byte{}, v.Bytes()...))
Joe Tsai2fc306a2019-06-20 03:01:22 -070080}