mirror of
https://github.com/ceph/ceph-csi.git
synced 2024-12-29 08:20:20 +00:00
360 lines
9.6 KiB
Go
360 lines
9.6 KiB
Go
|
// Copyright 2024 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 protolazy contains internal data structures for lazy message decoding.
|
||
|
package protolazy
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"sort"
|
||
|
|
||
|
"google.golang.org/protobuf/encoding/protowire"
|
||
|
piface "google.golang.org/protobuf/runtime/protoiface"
|
||
|
)
|
||
|
|
||
|
// IndexEntry is the structure for an index of the fields in a message of a
|
||
|
// proto (not descending to sub-messages)
|
||
|
type IndexEntry struct {
|
||
|
FieldNum uint32
|
||
|
// first byte of this tag/field
|
||
|
Start uint32
|
||
|
// first byte after a contiguous sequence of bytes for this tag/field, which could
|
||
|
// include a single encoding of the field, or multiple encodings for the field
|
||
|
End uint32
|
||
|
// True if this protobuf segment includes multiple encodings of the field
|
||
|
MultipleContiguous bool
|
||
|
}
|
||
|
|
||
|
// XXX_lazyUnmarshalInfo has information about a particular lazily decoded message
|
||
|
//
|
||
|
// Deprecated: Do not use. This will be deleted in the near future.
|
||
|
type XXX_lazyUnmarshalInfo struct {
|
||
|
// Index of fields and their positions in the protobuf for this
|
||
|
// message. Make index be a pointer to a slice so it can be updated
|
||
|
// atomically. The index pointer is only set once (lazily when/if
|
||
|
// the index is first needed), and must always be SET and LOADED
|
||
|
// ATOMICALLY.
|
||
|
index *[]IndexEntry
|
||
|
// The protobuf associated with this lazily decoded message. It is
|
||
|
// only set during proto.Unmarshal(). It doesn't need to be set and
|
||
|
// loaded atomically, since any simultaneous set (Unmarshal) and read
|
||
|
// (during a get) would already be a race in the app code.
|
||
|
Protobuf []byte
|
||
|
// The flags present when Unmarshal was originally called for this particular message
|
||
|
unmarshalFlags piface.UnmarshalInputFlags
|
||
|
}
|
||
|
|
||
|
// The Buffer and SetBuffer methods let v2/internal/impl interact with
|
||
|
// XXX_lazyUnmarshalInfo via an interface, to avoid an import cycle.
|
||
|
|
||
|
// Buffer returns the lazy unmarshal buffer.
|
||
|
//
|
||
|
// Deprecated: Do not use. This will be deleted in the near future.
|
||
|
func (lazy *XXX_lazyUnmarshalInfo) Buffer() []byte {
|
||
|
return lazy.Protobuf
|
||
|
}
|
||
|
|
||
|
// SetBuffer sets the lazy unmarshal buffer.
|
||
|
//
|
||
|
// Deprecated: Do not use. This will be deleted in the near future.
|
||
|
func (lazy *XXX_lazyUnmarshalInfo) SetBuffer(b []byte) {
|
||
|
lazy.Protobuf = b
|
||
|
}
|
||
|
|
||
|
// SetUnmarshalFlags is called to set a copy of the original unmarshalInputFlags.
|
||
|
// The flags should reflect how Unmarshal was called.
|
||
|
func (lazy *XXX_lazyUnmarshalInfo) SetUnmarshalFlags(f piface.UnmarshalInputFlags) {
|
||
|
lazy.unmarshalFlags = f
|
||
|
}
|
||
|
|
||
|
// UnmarshalFlags returns the original unmarshalInputFlags.
|
||
|
func (lazy *XXX_lazyUnmarshalInfo) UnmarshalFlags() piface.UnmarshalInputFlags {
|
||
|
return lazy.unmarshalFlags
|
||
|
}
|
||
|
|
||
|
// AllowedPartial returns true if the user originally unmarshalled this message with
|
||
|
// AllowPartial set to true
|
||
|
func (lazy *XXX_lazyUnmarshalInfo) AllowedPartial() bool {
|
||
|
return (lazy.unmarshalFlags & piface.UnmarshalCheckRequired) == 0
|
||
|
}
|
||
|
|
||
|
func protoFieldNumber(tag uint32) uint32 {
|
||
|
return tag >> 3
|
||
|
}
|
||
|
|
||
|
// buildIndex builds an index of the specified protobuf, return the index
|
||
|
// array and an error.
|
||
|
func buildIndex(buf []byte) ([]IndexEntry, error) {
|
||
|
index := make([]IndexEntry, 0, 16)
|
||
|
var lastProtoFieldNum uint32
|
||
|
var outOfOrder bool
|
||
|
|
||
|
var r BufferReader = NewBufferReader(buf)
|
||
|
|
||
|
for !r.Done() {
|
||
|
var tag uint32
|
||
|
var err error
|
||
|
var curPos = r.Pos
|
||
|
// INLINED: tag, err = r.DecodeVarint32()
|
||
|
{
|
||
|
i := r.Pos
|
||
|
buf := r.Buf
|
||
|
|
||
|
if i >= len(buf) {
|
||
|
return nil, errOutOfBounds
|
||
|
} else if buf[i] < 0x80 {
|
||
|
r.Pos++
|
||
|
tag = uint32(buf[i])
|
||
|
} else if r.Remaining() < 5 {
|
||
|
var v uint64
|
||
|
v, err = r.DecodeVarintSlow()
|
||
|
tag = uint32(v)
|
||
|
} else {
|
||
|
var v uint32
|
||
|
// we already checked the first byte
|
||
|
tag = uint32(buf[i]) & 127
|
||
|
i++
|
||
|
|
||
|
v = uint32(buf[i])
|
||
|
i++
|
||
|
tag |= (v & 127) << 7
|
||
|
if v < 128 {
|
||
|
goto done
|
||
|
}
|
||
|
|
||
|
v = uint32(buf[i])
|
||
|
i++
|
||
|
tag |= (v & 127) << 14
|
||
|
if v < 128 {
|
||
|
goto done
|
||
|
}
|
||
|
|
||
|
v = uint32(buf[i])
|
||
|
i++
|
||
|
tag |= (v & 127) << 21
|
||
|
if v < 128 {
|
||
|
goto done
|
||
|
}
|
||
|
|
||
|
v = uint32(buf[i])
|
||
|
i++
|
||
|
tag |= (v & 127) << 28
|
||
|
if v < 128 {
|
||
|
goto done
|
||
|
}
|
||
|
|
||
|
return nil, errOutOfBounds
|
||
|
|
||
|
done:
|
||
|
r.Pos = i
|
||
|
}
|
||
|
}
|
||
|
// DONE: tag, err = r.DecodeVarint32()
|
||
|
|
||
|
fieldNum := protoFieldNumber(tag)
|
||
|
if fieldNum < lastProtoFieldNum {
|
||
|
outOfOrder = true
|
||
|
}
|
||
|
|
||
|
// Skip the current value -- will skip over an entire group as well.
|
||
|
// INLINED: err = r.SkipValue(tag)
|
||
|
wireType := tag & 0x7
|
||
|
switch protowire.Type(wireType) {
|
||
|
case protowire.VarintType:
|
||
|
// INLINED: err = r.SkipVarint()
|
||
|
i := r.Pos
|
||
|
|
||
|
if len(r.Buf)-i < 10 {
|
||
|
// Use DecodeVarintSlow() to skip while
|
||
|
// checking for buffer overflow, but ignore result
|
||
|
_, err = r.DecodeVarintSlow()
|
||
|
goto out2
|
||
|
}
|
||
|
if r.Buf[i] < 0x80 {
|
||
|
goto out
|
||
|
}
|
||
|
i++
|
||
|
|
||
|
if r.Buf[i] < 0x80 {
|
||
|
goto out
|
||
|
}
|
||
|
i++
|
||
|
|
||
|
if r.Buf[i] < 0x80 {
|
||
|
goto out
|
||
|
}
|
||
|
i++
|
||
|
|
||
|
if r.Buf[i] < 0x80 {
|
||
|
goto out
|
||
|
}
|
||
|
i++
|
||
|
|
||
|
if r.Buf[i] < 0x80 {
|
||
|
goto out
|
||
|
}
|
||
|
i++
|
||
|
|
||
|
if r.Buf[i] < 0x80 {
|
||
|
goto out
|
||
|
}
|
||
|
i++
|
||
|
|
||
|
if r.Buf[i] < 0x80 {
|
||
|
goto out
|
||
|
}
|
||
|
i++
|
||
|
|
||
|
if r.Buf[i] < 0x80 {
|
||
|
goto out
|
||
|
}
|
||
|
i++
|
||
|
|
||
|
if r.Buf[i] < 0x80 {
|
||
|
goto out
|
||
|
}
|
||
|
i++
|
||
|
|
||
|
if r.Buf[i] < 0x80 {
|
||
|
goto out
|
||
|
}
|
||
|
return nil, errOverflow
|
||
|
out:
|
||
|
r.Pos = i + 1
|
||
|
// DONE: err = r.SkipVarint()
|
||
|
case protowire.Fixed64Type:
|
||
|
err = r.SkipFixed64()
|
||
|
case protowire.BytesType:
|
||
|
var n uint32
|
||
|
n, err = r.DecodeVarint32()
|
||
|
if err == nil {
|
||
|
err = r.Skip(int(n))
|
||
|
}
|
||
|
case protowire.StartGroupType:
|
||
|
err = r.SkipGroup(tag)
|
||
|
case protowire.Fixed32Type:
|
||
|
err = r.SkipFixed32()
|
||
|
default:
|
||
|
err = fmt.Errorf("Unexpected wire type (%d)", wireType)
|
||
|
}
|
||
|
// DONE: err = r.SkipValue(tag)
|
||
|
|
||
|
out2:
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
if fieldNum != lastProtoFieldNum {
|
||
|
index = append(index, IndexEntry{FieldNum: fieldNum,
|
||
|
Start: uint32(curPos),
|
||
|
End: uint32(r.Pos)},
|
||
|
)
|
||
|
} else {
|
||
|
index[len(index)-1].End = uint32(r.Pos)
|
||
|
index[len(index)-1].MultipleContiguous = true
|
||
|
}
|
||
|
lastProtoFieldNum = fieldNum
|
||
|
}
|
||
|
if outOfOrder {
|
||
|
sort.Slice(index, func(i, j int) bool {
|
||
|
return index[i].FieldNum < index[j].FieldNum ||
|
||
|
(index[i].FieldNum == index[j].FieldNum &&
|
||
|
index[i].Start < index[j].Start)
|
||
|
})
|
||
|
}
|
||
|
return index, nil
|
||
|
}
|
||
|
|
||
|
func (lazy *XXX_lazyUnmarshalInfo) SizeField(num uint32) (size int) {
|
||
|
start, end, found, _, multipleEntries := lazy.FindFieldInProto(num)
|
||
|
if multipleEntries != nil {
|
||
|
for _, entry := range multipleEntries {
|
||
|
size += int(entry.End - entry.Start)
|
||
|
}
|
||
|
return size
|
||
|
}
|
||
|
if !found {
|
||
|
return 0
|
||
|
}
|
||
|
return int(end - start)
|
||
|
}
|
||
|
|
||
|
func (lazy *XXX_lazyUnmarshalInfo) AppendField(b []byte, num uint32) ([]byte, bool) {
|
||
|
start, end, found, _, multipleEntries := lazy.FindFieldInProto(num)
|
||
|
if multipleEntries != nil {
|
||
|
for _, entry := range multipleEntries {
|
||
|
b = append(b, lazy.Protobuf[entry.Start:entry.End]...)
|
||
|
}
|
||
|
return b, true
|
||
|
}
|
||
|
if !found {
|
||
|
return nil, false
|
||
|
}
|
||
|
b = append(b, lazy.Protobuf[start:end]...)
|
||
|
return b, true
|
||
|
}
|
||
|
|
||
|
func (lazy *XXX_lazyUnmarshalInfo) SetIndex(index []IndexEntry) {
|
||
|
atomicStoreIndex(&lazy.index, &index)
|
||
|
}
|
||
|
|
||
|
// FindFieldInProto looks for field fieldNum in lazyUnmarshalInfo information
|
||
|
// (including protobuf), returns startOffset/endOffset/found.
|
||
|
func (lazy *XXX_lazyUnmarshalInfo) FindFieldInProto(fieldNum uint32) (start, end uint32, found, multipleContiguous bool, multipleEntries []IndexEntry) {
|
||
|
if lazy.Protobuf == nil {
|
||
|
// There is no backing protobuf for this message -- it was made from a builder
|
||
|
return 0, 0, false, false, nil
|
||
|
}
|
||
|
index := atomicLoadIndex(&lazy.index)
|
||
|
if index == nil {
|
||
|
r, err := buildIndex(lazy.Protobuf)
|
||
|
if err != nil {
|
||
|
panic(fmt.Sprintf("findFieldInfo: error building index when looking for field %d: %v", fieldNum, err))
|
||
|
}
|
||
|
// lazy.index is a pointer to the slice returned by BuildIndex
|
||
|
index = &r
|
||
|
atomicStoreIndex(&lazy.index, index)
|
||
|
}
|
||
|
return lookupField(index, fieldNum)
|
||
|
}
|
||
|
|
||
|
// lookupField returns the offset at which the indicated field starts using
|
||
|
// the index, offset immediately after field ends (including all instances of
|
||
|
// a repeated field), and bools indicating if field was found and if there
|
||
|
// are multiple encodings of the field in the byte range.
|
||
|
//
|
||
|
// To hande the uncommon case where there are repeated encodings for the same
|
||
|
// field which are not consecutive in the protobuf (so we need to returns
|
||
|
// multiple start/end offsets), we also return a slice multipleEntries. If
|
||
|
// multipleEntries is non-nil, then multiple entries were found, and the
|
||
|
// values in the slice should be used, rather than start/end/found.
|
||
|
func lookupField(indexp *[]IndexEntry, fieldNum uint32) (start, end uint32, found bool, multipleContiguous bool, multipleEntries []IndexEntry) {
|
||
|
// The pointer indexp to the index was already loaded atomically.
|
||
|
// The slice is uniquely associated with the pointer, so it doesn't
|
||
|
// need to be loaded atomically.
|
||
|
index := *indexp
|
||
|
for i, entry := range index {
|
||
|
if fieldNum == entry.FieldNum {
|
||
|
if i < len(index)-1 && entry.FieldNum == index[i+1].FieldNum {
|
||
|
// Handle the uncommon case where there are
|
||
|
// repeated entries for the same field which
|
||
|
// are not contiguous in the protobuf.
|
||
|
multiple := make([]IndexEntry, 1, 2)
|
||
|
multiple[0] = IndexEntry{fieldNum, entry.Start, entry.End, entry.MultipleContiguous}
|
||
|
i++
|
||
|
for i < len(index) && index[i].FieldNum == fieldNum {
|
||
|
multiple = append(multiple, IndexEntry{fieldNum, index[i].Start, index[i].End, index[i].MultipleContiguous})
|
||
|
i++
|
||
|
}
|
||
|
return 0, 0, false, false, multiple
|
||
|
|
||
|
}
|
||
|
return entry.Start, entry.End, true, entry.MultipleContiguous, nil
|
||
|
}
|
||
|
if fieldNum < entry.FieldNum {
|
||
|
return 0, 0, false, false, nil
|
||
|
}
|
||
|
}
|
||
|
return 0, 0, false, false, nil
|
||
|
}
|