protobuf-go/internal/impl/legacy_unknown.go
Joe Tsai 00a323deed internal/impl: use XXX_unrecognized exclusively for unknown fields
The protobuf data model makes no distinction between unknown fields
that are within the extension field ranges or not. Now that we eagerly
unmarshal extensions, there is even less need for storing unknown
fields in the extension map. Instead, use the XXX_unrecognized field
exclusively for this purpose.

Change-Id: I673a7d6259fe9fdbdc295bcfa8252ef4da415343
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/175579
Reviewed-by: Damien Neil <dneil@google.com>
2019-05-09 01:26:50 +00:00

130 lines
3.1 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"
"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)
return func(p *messageDataType) pref.UnknownFields {
if p.p.IsNil() {
return emptyUnknownFields{}
}
rv := p.p.Apply(fieldOffset).AsValueOf(bytesType)
return (*legacyUnknownBytes)(rv.Interface().(*[]byte))
}
}
// 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
}