2019-04-01 20:49:56 +00:00
|
|
|
// Copyright 2019 The Go Authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
package impl
|
|
|
|
|
|
|
|
import (
|
|
|
|
"sort"
|
|
|
|
"sync/atomic"
|
|
|
|
|
|
|
|
"google.golang.org/protobuf/internal/errors"
|
|
|
|
proto "google.golang.org/protobuf/proto"
|
|
|
|
pref "google.golang.org/protobuf/reflect/protoreflect"
|
|
|
|
piface "google.golang.org/protobuf/runtime/protoiface"
|
|
|
|
)
|
|
|
|
|
|
|
|
type marshalOptions uint
|
|
|
|
|
|
|
|
const (
|
|
|
|
marshalAllowPartial marshalOptions = 1 << iota
|
|
|
|
marshalDeterministic
|
|
|
|
marshalUseCachedSize
|
|
|
|
)
|
|
|
|
|
|
|
|
func newMarshalOptions(opts piface.MarshalOptions) marshalOptions {
|
|
|
|
var o marshalOptions
|
|
|
|
if opts.AllowPartial {
|
|
|
|
o |= marshalAllowPartial
|
|
|
|
}
|
|
|
|
if opts.Deterministic {
|
|
|
|
o |= marshalDeterministic
|
|
|
|
}
|
|
|
|
if opts.UseCachedSize {
|
|
|
|
o |= marshalUseCachedSize
|
|
|
|
}
|
|
|
|
return o
|
|
|
|
}
|
|
|
|
|
|
|
|
func (o marshalOptions) Options() proto.MarshalOptions {
|
|
|
|
return proto.MarshalOptions{
|
|
|
|
AllowPartial: o.AllowPartial(),
|
|
|
|
Deterministic: o.Deterministic(),
|
|
|
|
UseCachedSize: o.UseCachedSize(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (o marshalOptions) AllowPartial() bool { return o&marshalAllowPartial != 0 }
|
|
|
|
func (o marshalOptions) Deterministic() bool { return o&marshalDeterministic != 0 }
|
|
|
|
func (o marshalOptions) UseCachedSize() bool { return o&marshalUseCachedSize != 0 }
|
|
|
|
|
|
|
|
// size is protoreflect.Methods.Size.
|
2019-05-22 09:12:36 +00:00
|
|
|
func (mi *MessageInfo) size(msg pref.ProtoMessage) (size int) {
|
2019-04-01 20:49:56 +00:00
|
|
|
return mi.sizePointer(pointerOfIface(msg), 0)
|
|
|
|
}
|
|
|
|
|
2019-05-22 09:12:36 +00:00
|
|
|
func (mi *MessageInfo) sizePointer(p pointer, opts marshalOptions) (size int) {
|
2019-04-01 20:49:56 +00:00
|
|
|
mi.init()
|
|
|
|
if p.IsNil() {
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
if opts.UseCachedSize() && mi.sizecacheOffset.IsValid() {
|
|
|
|
return int(atomic.LoadInt32(p.Apply(mi.sizecacheOffset).Int32()))
|
|
|
|
}
|
|
|
|
return mi.sizePointerSlow(p, opts)
|
|
|
|
}
|
|
|
|
|
2019-05-22 09:12:36 +00:00
|
|
|
func (mi *MessageInfo) sizePointerSlow(p pointer, opts marshalOptions) (size int) {
|
2019-04-01 20:49:56 +00:00
|
|
|
if mi.extensionOffset.IsValid() {
|
|
|
|
e := p.Apply(mi.extensionOffset).Extensions()
|
|
|
|
size += mi.sizeExtensions(e, opts)
|
|
|
|
}
|
|
|
|
for _, f := range mi.fieldsOrdered {
|
|
|
|
fptr := p.Apply(f.offset)
|
|
|
|
if f.isPointer && fptr.Elem().IsNil() {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if f.funcs.size == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
size += f.funcs.size(fptr, f.tagsize, opts)
|
|
|
|
}
|
|
|
|
if mi.unknownOffset.IsValid() {
|
|
|
|
u := *p.Apply(mi.unknownOffset).Bytes()
|
|
|
|
size += len(u)
|
|
|
|
}
|
|
|
|
if mi.sizecacheOffset.IsValid() {
|
|
|
|
atomic.StoreInt32(p.Apply(mi.sizecacheOffset).Int32(), int32(size))
|
|
|
|
}
|
|
|
|
return size
|
|
|
|
}
|
|
|
|
|
|
|
|
// marshalAppend is protoreflect.Methods.MarshalAppend.
|
2019-05-22 09:12:36 +00:00
|
|
|
func (mi *MessageInfo) marshalAppend(b []byte, msg pref.ProtoMessage, opts piface.MarshalOptions) ([]byte, error) {
|
2019-04-01 20:49:56 +00:00
|
|
|
return mi.marshalAppendPointer(b, pointerOfIface(msg), newMarshalOptions(opts))
|
|
|
|
}
|
|
|
|
|
2019-05-22 09:12:36 +00:00
|
|
|
func (mi *MessageInfo) marshalAppendPointer(b []byte, p pointer, opts marshalOptions) ([]byte, error) {
|
2019-04-01 20:49:56 +00:00
|
|
|
mi.init()
|
|
|
|
if p.IsNil() {
|
|
|
|
return b, nil
|
|
|
|
}
|
|
|
|
var err error
|
|
|
|
var nerr errors.NonFatal
|
|
|
|
// The old marshaler encodes extensions at beginning.
|
|
|
|
if mi.extensionOffset.IsValid() {
|
|
|
|
e := p.Apply(mi.extensionOffset).Extensions()
|
|
|
|
// TODO: Special handling for MessageSet?
|
|
|
|
b, err = mi.appendExtensions(b, e, opts)
|
|
|
|
if !nerr.Merge(err) {
|
|
|
|
return b, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for _, f := range mi.fieldsOrdered {
|
|
|
|
fptr := p.Apply(f.offset)
|
|
|
|
if f.isPointer && fptr.Elem().IsNil() {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if f.funcs.marshal == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
b, err = f.funcs.marshal(b, fptr, f.wiretag, opts)
|
|
|
|
if !nerr.Merge(err) {
|
|
|
|
return b, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if mi.unknownOffset.IsValid() {
|
|
|
|
u := *p.Apply(mi.unknownOffset).Bytes()
|
|
|
|
b = append(b, u...)
|
|
|
|
}
|
|
|
|
return b, nerr.E
|
|
|
|
}
|
|
|
|
|
2019-05-22 09:12:36 +00:00
|
|
|
func (mi *MessageInfo) sizeExtensions(ext *legacyExtensionMap, opts marshalOptions) (n int) {
|
2019-04-01 20:49:56 +00:00
|
|
|
if ext == nil {
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
for _, e := range *ext {
|
|
|
|
ei := mi.extensionFieldInfo(e.Desc)
|
|
|
|
if ei.funcs.size == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
n += ei.funcs.size(e.value, ei.tagsize, opts)
|
|
|
|
}
|
|
|
|
return n
|
|
|
|
}
|
|
|
|
|
2019-05-22 09:12:36 +00:00
|
|
|
func (mi *MessageInfo) appendExtensions(b []byte, ext *legacyExtensionMap, opts marshalOptions) ([]byte, error) {
|
2019-04-01 20:49:56 +00:00
|
|
|
if ext == nil {
|
|
|
|
return b, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
switch len(*ext) {
|
|
|
|
case 0:
|
|
|
|
return b, nil
|
|
|
|
case 1:
|
|
|
|
// Fast-path for one extension: Don't bother sorting the keys.
|
|
|
|
var err error
|
|
|
|
for _, e := range *ext {
|
|
|
|
ei := mi.extensionFieldInfo(e.Desc)
|
|
|
|
b, err = ei.funcs.marshal(b, e.value, ei.wiretag, opts)
|
|
|
|
}
|
|
|
|
return b, err
|
|
|
|
default:
|
|
|
|
// Sort the keys to provide a deterministic encoding.
|
|
|
|
// Not sure this is required, but the old code does it.
|
|
|
|
keys := make([]int, 0, len(*ext))
|
|
|
|
for k := range *ext {
|
|
|
|
keys = append(keys, int(k))
|
|
|
|
}
|
|
|
|
sort.Ints(keys)
|
|
|
|
var err error
|
|
|
|
var nerr errors.NonFatal
|
|
|
|
for _, k := range keys {
|
|
|
|
e := (*ext)[int32(k)]
|
|
|
|
ei := mi.extensionFieldInfo(e.Desc)
|
|
|
|
b, err = ei.funcs.marshal(b, e.value, ei.wiretag, opts)
|
|
|
|
if !nerr.Merge(err) {
|
|
|
|
return b, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return b, nerr.E
|
|
|
|
}
|
|
|
|
}
|