mirror of
https://github.com/protocolbuffers/protobuf-go.git
synced 2025-02-04 21:39:58 +00:00
095e23da31
Both legacy_unknown.go and legacy_extension.go deal with the current data structure for unknown fields and extensions. While the current data structure is sub-optimial, strictly speaking, they are not "legacy" since they are very much used by the v2 API as well. Change-Id: I3852dba0dcebaee00bbeb94d9936cd759f774f0a Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/178477 Reviewed-by: Damien Neil <dneil@google.com>
130 lines
3.0 KiB
Go
130 lines
3.0 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"
|
|
|
|
"google.golang.org/protobuf/internal/encoding/wire"
|
|
pref "google.golang.org/protobuf/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
|
|
}
|