blob: c793021f527bfa6463202523b7770c73428c3f5e [file] [log] [blame]
Damien Neilc37adef2019-04-01 13:49:56 -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 impl
6
7import (
8 "sort"
9 "sync/atomic"
10
Damien Neilce3384c2019-11-06 13:18:28 -080011 "google.golang.org/protobuf/internal/flags"
Damien Neilc37adef2019-04-01 13:49:56 -070012 proto "google.golang.org/protobuf/proto"
13 pref "google.golang.org/protobuf/reflect/protoreflect"
14 piface "google.golang.org/protobuf/runtime/protoiface"
15)
16
Damien Neile91877d2019-06-27 10:54:42 -070017// marshalOptions is a more efficient representation of MarshalOptions.
18//
19// We don't preserve the AllowPartial flag, because fast-path (un)marshal
20// operations always allow partial messages.
Damien Neilc37adef2019-04-01 13:49:56 -070021type marshalOptions uint
22
23const (
Damien Neile91877d2019-06-27 10:54:42 -070024 marshalDeterministic marshalOptions = 1 << iota
Damien Neilc37adef2019-04-01 13:49:56 -070025 marshalUseCachedSize
26)
27
28func newMarshalOptions(opts piface.MarshalOptions) marshalOptions {
29 var o marshalOptions
Damien Neilc37adef2019-04-01 13:49:56 -070030 if opts.Deterministic {
31 o |= marshalDeterministic
32 }
33 if opts.UseCachedSize {
34 o |= marshalUseCachedSize
35 }
36 return o
37}
38
39func (o marshalOptions) Options() proto.MarshalOptions {
40 return proto.MarshalOptions{
Damien Neile91877d2019-06-27 10:54:42 -070041 AllowPartial: true,
Damien Neilc37adef2019-04-01 13:49:56 -070042 Deterministic: o.Deterministic(),
43 UseCachedSize: o.UseCachedSize(),
44 }
45}
46
Damien Neilc37adef2019-04-01 13:49:56 -070047func (o marshalOptions) Deterministic() bool { return o&marshalDeterministic != 0 }
48func (o marshalOptions) UseCachedSize() bool { return o&marshalUseCachedSize != 0 }
49
50// size is protoreflect.Methods.Size.
Joe Tsaif8b855d2019-07-12 13:37:59 -070051func (mi *MessageInfo) size(m pref.Message, opts piface.MarshalOptions) (size int) {
Joe Tsai0f81b382019-07-10 23:14:31 -070052 var p pointer
53 if ms, ok := m.(*messageState); ok {
54 p = ms.pointer()
55 } else {
56 p = m.(*messageReflectWrapper).pointer()
57 }
Joe Tsaif8b855d2019-07-12 13:37:59 -070058 return mi.sizePointer(p, newMarshalOptions(opts))
Damien Neilc37adef2019-04-01 13:49:56 -070059}
60
Joe Tsai4fe96632019-05-22 05:12:36 -040061func (mi *MessageInfo) sizePointer(p pointer, opts marshalOptions) (size int) {
Damien Neilc37adef2019-04-01 13:49:56 -070062 mi.init()
63 if p.IsNil() {
64 return 0
65 }
66 if opts.UseCachedSize() && mi.sizecacheOffset.IsValid() {
67 return int(atomic.LoadInt32(p.Apply(mi.sizecacheOffset).Int32()))
68 }
69 return mi.sizePointerSlow(p, opts)
70}
71
Joe Tsai4fe96632019-05-22 05:12:36 -040072func (mi *MessageInfo) sizePointerSlow(p pointer, opts marshalOptions) (size int) {
Damien Neilce3384c2019-11-06 13:18:28 -080073 if flags.ProtoLegacy && mi.isMessageSet {
74 size = sizeMessageSet(mi, p, opts)
75 if mi.sizecacheOffset.IsValid() {
76 atomic.StoreInt32(p.Apply(mi.sizecacheOffset).Int32(), int32(size))
77 }
78 return size
79 }
Damien Neilc37adef2019-04-01 13:49:56 -070080 if mi.extensionOffset.IsValid() {
81 e := p.Apply(mi.extensionOffset).Extensions()
82 size += mi.sizeExtensions(e, opts)
83 }
Damien Neil4ae30bb2019-06-20 10:12:23 -070084 for _, f := range mi.orderedCoderFields {
Damien Neilce413af2019-12-05 16:36:28 -080085 if f.funcs.size == nil {
Damien Neilc37adef2019-04-01 13:49:56 -070086 continue
87 }
Damien Neilce413af2019-12-05 16:36:28 -080088 fptr := p.Apply(f.offset)
89 if f.isPointer && fptr.Elem().IsNil() {
Damien Neilc37adef2019-04-01 13:49:56 -070090 continue
91 }
92 size += f.funcs.size(fptr, f.tagsize, opts)
93 }
94 if mi.unknownOffset.IsValid() {
95 u := *p.Apply(mi.unknownOffset).Bytes()
96 size += len(u)
97 }
98 if mi.sizecacheOffset.IsValid() {
99 atomic.StoreInt32(p.Apply(mi.sizecacheOffset).Int32(), int32(size))
100 }
101 return size
102}
103
104// marshalAppend is protoreflect.Methods.MarshalAppend.
Joe Tsai0f81b382019-07-10 23:14:31 -0700105func (mi *MessageInfo) marshalAppend(b []byte, m pref.Message, opts piface.MarshalOptions) ([]byte, error) {
106 var p pointer
107 if ms, ok := m.(*messageState); ok {
108 p = ms.pointer()
109 } else {
110 p = m.(*messageReflectWrapper).pointer()
111 }
112 return mi.marshalAppendPointer(b, p, newMarshalOptions(opts))
Damien Neilc37adef2019-04-01 13:49:56 -0700113}
114
Joe Tsai4fe96632019-05-22 05:12:36 -0400115func (mi *MessageInfo) marshalAppendPointer(b []byte, p pointer, opts marshalOptions) ([]byte, error) {
Damien Neilc37adef2019-04-01 13:49:56 -0700116 mi.init()
117 if p.IsNil() {
118 return b, nil
119 }
Damien Neilce3384c2019-11-06 13:18:28 -0800120 if flags.ProtoLegacy && mi.isMessageSet {
121 return marshalMessageSet(mi, b, p, opts)
122 }
Damien Neilc37adef2019-04-01 13:49:56 -0700123 var err error
Damien Neilc37adef2019-04-01 13:49:56 -0700124 // The old marshaler encodes extensions at beginning.
125 if mi.extensionOffset.IsValid() {
126 e := p.Apply(mi.extensionOffset).Extensions()
127 // TODO: Special handling for MessageSet?
128 b, err = mi.appendExtensions(b, e, opts)
Damien Neil8c86fc52019-06-19 09:28:29 -0700129 if err != nil {
Damien Neilc37adef2019-04-01 13:49:56 -0700130 return b, err
131 }
132 }
Damien Neil4ae30bb2019-06-20 10:12:23 -0700133 for _, f := range mi.orderedCoderFields {
Damien Neilce413af2019-12-05 16:36:28 -0800134 if f.funcs.marshal == nil {
Damien Neilc37adef2019-04-01 13:49:56 -0700135 continue
136 }
Damien Neilce413af2019-12-05 16:36:28 -0800137 fptr := p.Apply(f.offset)
138 if f.isPointer && fptr.Elem().IsNil() {
Damien Neilc37adef2019-04-01 13:49:56 -0700139 continue
140 }
141 b, err = f.funcs.marshal(b, fptr, f.wiretag, opts)
Damien Neil8c86fc52019-06-19 09:28:29 -0700142 if err != nil {
Damien Neilc37adef2019-04-01 13:49:56 -0700143 return b, err
144 }
145 }
Damien Neilce3384c2019-11-06 13:18:28 -0800146 if mi.unknownOffset.IsValid() && !mi.isMessageSet {
Damien Neilc37adef2019-04-01 13:49:56 -0700147 u := *p.Apply(mi.unknownOffset).Bytes()
148 b = append(b, u...)
149 }
Damien Neil8c86fc52019-06-19 09:28:29 -0700150 return b, nil
Damien Neilc37adef2019-04-01 13:49:56 -0700151}
152
Joe Tsai89d49632019-06-04 16:20:00 -0700153func (mi *MessageInfo) sizeExtensions(ext *map[int32]ExtensionField, opts marshalOptions) (n int) {
Damien Neilc37adef2019-04-01 13:49:56 -0700154 if ext == nil {
155 return 0
156 }
Joe Tsai89d49632019-06-04 16:20:00 -0700157 for _, x := range *ext {
Damien Neil79571e92019-12-09 10:24:36 -0800158 xi := getExtensionFieldInfo(x.Type())
Joe Tsai89d49632019-06-04 16:20:00 -0700159 if xi.funcs.size == nil {
Damien Neilc37adef2019-04-01 13:49:56 -0700160 continue
161 }
Damien Neil68b81c32019-08-22 11:41:32 -0700162 n += xi.funcs.size(x.Value(), xi.tagsize, opts)
Damien Neilc37adef2019-04-01 13:49:56 -0700163 }
164 return n
165}
166
Joe Tsai89d49632019-06-04 16:20:00 -0700167func (mi *MessageInfo) appendExtensions(b []byte, ext *map[int32]ExtensionField, opts marshalOptions) ([]byte, error) {
Damien Neilc37adef2019-04-01 13:49:56 -0700168 if ext == nil {
169 return b, nil
170 }
171
172 switch len(*ext) {
173 case 0:
174 return b, nil
175 case 1:
176 // Fast-path for one extension: Don't bother sorting the keys.
177 var err error
Joe Tsai89d49632019-06-04 16:20:00 -0700178 for _, x := range *ext {
Damien Neil79571e92019-12-09 10:24:36 -0800179 xi := getExtensionFieldInfo(x.Type())
Damien Neil68b81c32019-08-22 11:41:32 -0700180 b, err = xi.funcs.marshal(b, x.Value(), xi.wiretag, opts)
Damien Neilc37adef2019-04-01 13:49:56 -0700181 }
182 return b, err
183 default:
184 // Sort the keys to provide a deterministic encoding.
185 // Not sure this is required, but the old code does it.
186 keys := make([]int, 0, len(*ext))
187 for k := range *ext {
188 keys = append(keys, int(k))
189 }
190 sort.Ints(keys)
191 var err error
Damien Neilc37adef2019-04-01 13:49:56 -0700192 for _, k := range keys {
Joe Tsai89d49632019-06-04 16:20:00 -0700193 x := (*ext)[int32(k)]
Damien Neil79571e92019-12-09 10:24:36 -0800194 xi := getExtensionFieldInfo(x.Type())
Damien Neil68b81c32019-08-22 11:41:32 -0700195 b, err = xi.funcs.marshal(b, x.Value(), xi.wiretag, opts)
Damien Neil8c86fc52019-06-19 09:28:29 -0700196 if err != nil {
Damien Neilc37adef2019-04-01 13:49:56 -0700197 return b, err
198 }
199 }
Damien Neil8c86fc52019-06-19 09:28:29 -0700200 return b, nil
Damien Neilc37adef2019-04-01 13:49:56 -0700201 }
202}