mirror of
https://github.com/protocolbuffers/protobuf-go.git
synced 2025-01-06 10:01:25 +00:00
6cf80c4f76
Similar to how generated messages allow you to call Get methods on a nil pointer, we permit similar functionality when protobuf reflection is used on a nil pointer. Change-Id: Ie2f596d39105c191073b42d7d689525c3b715240 Reviewed-on: https://go-review.googlesource.com/c/152021 Reviewed-by: Damien Neil <dneil@google.com>
217 lines
5.2 KiB
Go
217 lines
5.2 KiB
Go
// Copyright 2018 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 (
|
|
"container/list"
|
|
"reflect"
|
|
"sort"
|
|
|
|
papi "github.com/golang/protobuf/protoapi"
|
|
"github.com/golang/protobuf/v2/internal/encoding/wire"
|
|
pref "github.com/golang/protobuf/v2/reflect/protoreflect"
|
|
)
|
|
|
|
var bytesType = reflect.TypeOf([]byte(nil))
|
|
|
|
func makeLegacyUnknownFieldsFunc(t reflect.Type) func(p *messageDataType) pref.UnknownFields {
|
|
fu, ok := t.FieldByName("XXX_unrecognized")
|
|
if !ok || fu.Type != bytesType {
|
|
return nil
|
|
}
|
|
fieldOffset := offsetOf(fu)
|
|
unkFunc := func(p *messageDataType) pref.UnknownFields {
|
|
if p.p.IsNil() {
|
|
return emptyUnknownFields{}
|
|
}
|
|
rv := p.p.Apply(fieldOffset).AsValueOf(bytesType)
|
|
return (*legacyUnknownBytes)(rv.Interface().(*[]byte))
|
|
}
|
|
extFunc := makeLegacyExtensionMapFunc(t)
|
|
if extFunc != nil {
|
|
return func(p *messageDataType) pref.UnknownFields {
|
|
if p.p.IsNil() {
|
|
return emptyUnknownFields{}
|
|
}
|
|
return &legacyUnknownBytesAndExtensionMap{
|
|
unkFunc(p), extFunc(p), p.mi.Type.ExtensionRanges(),
|
|
}
|
|
}
|
|
}
|
|
return unkFunc
|
|
}
|
|
|
|
// legacyUnknownBytesAndExtensionMap is a wrapper around both XXX_unrecognized
|
|
// and also the extension field map.
|
|
type legacyUnknownBytesAndExtensionMap struct {
|
|
u pref.UnknownFields
|
|
x papi.ExtensionFields
|
|
r pref.FieldRanges
|
|
}
|
|
|
|
func (fs *legacyUnknownBytesAndExtensionMap) Len() int {
|
|
n := fs.u.Len()
|
|
fs.x.Range(func(_ pref.FieldNumber, x papi.ExtensionField) bool {
|
|
if len(x.Raw) > 0 {
|
|
n++
|
|
}
|
|
return true
|
|
})
|
|
return n
|
|
}
|
|
|
|
func (fs *legacyUnknownBytesAndExtensionMap) Get(num pref.FieldNumber) (raw pref.RawFields) {
|
|
if fs.r.Has(num) {
|
|
return fs.x.Get(num).Raw
|
|
}
|
|
return fs.u.Get(num)
|
|
}
|
|
|
|
func (fs *legacyUnknownBytesAndExtensionMap) Set(num pref.FieldNumber, raw pref.RawFields) {
|
|
if fs.r.Has(num) {
|
|
x := fs.x.Get(num)
|
|
x.Raw = raw
|
|
fs.x.Set(num, x)
|
|
return
|
|
}
|
|
fs.u.Set(num, raw)
|
|
}
|
|
|
|
func (fs *legacyUnknownBytesAndExtensionMap) Range(f func(pref.FieldNumber, pref.RawFields) bool) {
|
|
// Range over unknown fields not in the extension range.
|
|
// Create a closure around f to capture whether iteration terminated early.
|
|
var stop bool
|
|
fs.u.Range(func(n pref.FieldNumber, b pref.RawFields) bool {
|
|
stop = stop || !f(n, b)
|
|
return !stop
|
|
})
|
|
if stop {
|
|
return
|
|
}
|
|
|
|
// Range over unknown fields in the extension range in ascending order
|
|
// to ensure protoreflect.UnknownFields.Range remains deterministic.
|
|
type entry struct {
|
|
num pref.FieldNumber
|
|
raw pref.RawFields
|
|
}
|
|
var xs []entry
|
|
fs.x.Range(func(n pref.FieldNumber, x papi.ExtensionField) bool {
|
|
if len(x.Raw) > 0 {
|
|
xs = append(xs, entry{n, x.Raw})
|
|
}
|
|
return true
|
|
})
|
|
sort.Slice(xs, func(i, j int) bool { return xs[i].num < xs[j].num })
|
|
for _, x := range xs {
|
|
if !f(x.num, x.raw) {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
func (fs *legacyUnknownBytesAndExtensionMap) IsSupported() bool {
|
|
return true
|
|
}
|
|
|
|
// legacyUnknownBytes is a wrapper around XXX_unrecognized that implements
|
|
// the protoreflect.UnknownFields interface. This is challenging since we are
|
|
// limited to a []byte, so we do not have much flexibility in the choice
|
|
// of data structure that would have been ideal.
|
|
type legacyUnknownBytes []byte
|
|
|
|
func (fs *legacyUnknownBytes) Len() int {
|
|
// Runtime complexity: O(n)
|
|
b := *fs
|
|
m := map[pref.FieldNumber]bool{}
|
|
for len(b) > 0 {
|
|
num, _, n := wire.ConsumeField(b)
|
|
m[num] = true
|
|
b = b[n:]
|
|
}
|
|
return len(m)
|
|
}
|
|
|
|
func (fs *legacyUnknownBytes) Get(num pref.FieldNumber) (raw pref.RawFields) {
|
|
// Runtime complexity: O(n)
|
|
b := *fs
|
|
for len(b) > 0 {
|
|
num2, _, n := wire.ConsumeField(b)
|
|
if num == num2 {
|
|
raw = append(raw, b[:n]...)
|
|
}
|
|
b = b[n:]
|
|
}
|
|
return raw
|
|
}
|
|
|
|
func (fs *legacyUnknownBytes) Set(num pref.FieldNumber, raw pref.RawFields) {
|
|
num2, _, _ := wire.ConsumeTag(raw)
|
|
if len(raw) > 0 && (!raw.IsValid() || num != num2) {
|
|
panic("invalid raw fields")
|
|
}
|
|
|
|
// Remove all current fields of num.
|
|
// Runtime complexity: O(n)
|
|
b := *fs
|
|
out := (*fs)[:0]
|
|
for len(b) > 0 {
|
|
num2, _, n := wire.ConsumeField(b)
|
|
if num != num2 {
|
|
out = append(out, b[:n]...)
|
|
}
|
|
b = b[n:]
|
|
}
|
|
*fs = out
|
|
|
|
// Append new fields of num.
|
|
*fs = append(*fs, raw...)
|
|
}
|
|
|
|
func (fs *legacyUnknownBytes) Range(f func(pref.FieldNumber, pref.RawFields) bool) {
|
|
type entry struct {
|
|
num pref.FieldNumber
|
|
raw pref.RawFields
|
|
}
|
|
|
|
// Collect up a list of all the raw fields.
|
|
// We preserve the order such that the latest encountered fields
|
|
// are presented at the end.
|
|
//
|
|
// Runtime complexity: O(n)
|
|
b := *fs
|
|
l := list.New()
|
|
m := map[pref.FieldNumber]*list.Element{}
|
|
for len(b) > 0 {
|
|
num, _, n := wire.ConsumeField(b)
|
|
if e, ok := m[num]; ok {
|
|
x := e.Value.(*entry)
|
|
x.raw = append(x.raw, b[:n]...)
|
|
l.MoveToBack(e)
|
|
} else {
|
|
x := &entry{num: num}
|
|
x.raw = append(x.raw, b[:n]...)
|
|
m[num] = l.PushBack(x)
|
|
}
|
|
b = b[n:]
|
|
}
|
|
|
|
// Iterate over all the raw fields.
|
|
// This ranges over a snapshot of the current state such that mutations
|
|
// while ranging are not observable.
|
|
//
|
|
// Runtime complexity: O(n)
|
|
for e := l.Front(); e != nil; e = e.Next() {
|
|
x := e.Value.(*entry)
|
|
if !f(x.num, x.raw) {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
func (fs *legacyUnknownBytes) IsSupported() bool {
|
|
return true
|
|
}
|