// 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 ( piface ) // 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 ( *XXX_lazyUnmarshalInfo) () []byte { return .Protobuf } // SetBuffer sets the lazy unmarshal buffer. // // Deprecated: Do not use. This will be deleted in the near future. func ( *XXX_lazyUnmarshalInfo) ( []byte) { .Protobuf = } // SetUnmarshalFlags is called to set a copy of the original unmarshalInputFlags. // The flags should reflect how Unmarshal was called. func ( *XXX_lazyUnmarshalInfo) ( piface.UnmarshalInputFlags) { .unmarshalFlags = } // UnmarshalFlags returns the original unmarshalInputFlags. func ( *XXX_lazyUnmarshalInfo) () piface.UnmarshalInputFlags { return .unmarshalFlags } // AllowedPartial returns true if the user originally unmarshalled this message with // AllowPartial set to true func ( *XXX_lazyUnmarshalInfo) () bool { return (.unmarshalFlags & piface.UnmarshalCheckRequired) == 0 } func ( uint32) uint32 { return >> 3 } // buildIndex builds an index of the specified protobuf, return the index // array and an error. func ( []byte) ([]IndexEntry, error) { := make([]IndexEntry, 0, 16) var uint32 var bool var BufferReader = NewBufferReader() for !.Done() { var uint32 var error var = .Pos // INLINED: tag, err = r.DecodeVarint32() { := .Pos := .Buf if >= len() { return nil, errOutOfBounds } else if [] < 0x80 { .Pos++ = uint32([]) } else if .Remaining() < 5 { var uint64 , = .DecodeVarintSlow() = uint32() } else { var uint32 // we already checked the first byte = uint32([]) & 127 ++ = uint32([]) ++ |= ( & 127) << 7 if < 128 { goto } = uint32([]) ++ |= ( & 127) << 14 if < 128 { goto } = uint32([]) ++ |= ( & 127) << 21 if < 128 { goto } = uint32([]) ++ |= ( & 127) << 28 if < 128 { goto } return nil, errOutOfBounds : .Pos = } } // DONE: tag, err = r.DecodeVarint32() := protoFieldNumber() if < { = true } // Skip the current value -- will skip over an entire group as well. // INLINED: err = r.SkipValue(tag) := & 0x7 switch protowire.Type() { case protowire.VarintType: // INLINED: err = r.SkipVarint() := .Pos if len(.Buf)- < 10 { // Use DecodeVarintSlow() to skip while // checking for buffer overflow, but ignore result _, = .DecodeVarintSlow() goto } if .Buf[] < 0x80 { goto } ++ if .Buf[] < 0x80 { goto } ++ if .Buf[] < 0x80 { goto } ++ if .Buf[] < 0x80 { goto } ++ if .Buf[] < 0x80 { goto } ++ if .Buf[] < 0x80 { goto } ++ if .Buf[] < 0x80 { goto } ++ if .Buf[] < 0x80 { goto } ++ if .Buf[] < 0x80 { goto } ++ if .Buf[] < 0x80 { goto } return nil, errOverflow : .Pos = + 1 // DONE: err = r.SkipVarint() case protowire.Fixed64Type: = .SkipFixed64() case protowire.BytesType: var uint32 , = .DecodeVarint32() if == nil { = .Skip(int()) } case protowire.StartGroupType: = .SkipGroup() case protowire.Fixed32Type: = .SkipFixed32() default: = fmt.Errorf("Unexpected wire type (%d)", ) } // DONE: err = r.SkipValue(tag) : if != nil { return nil, } if != { = append(, IndexEntry{FieldNum: , Start: uint32(), End: uint32(.Pos)}, ) } else { [len()-1].End = uint32(.Pos) [len()-1].MultipleContiguous = true } = } if { sort.Slice(, func(, int) bool { return [].FieldNum < [].FieldNum || ([].FieldNum == [].FieldNum && [].Start < [].Start) }) } return , nil } func ( *XXX_lazyUnmarshalInfo) ( uint32) ( int) { , , , , := .FindFieldInProto() if != nil { for , := range { += int(.End - .Start) } return } if ! { return 0 } return int( - ) } func ( *XXX_lazyUnmarshalInfo) ( []byte, uint32) ([]byte, bool) { , , , , := .FindFieldInProto() if != nil { for , := range { = append(, .Protobuf[.Start:.End]...) } return , true } if ! { return nil, false } = append(, .Protobuf[:]...) return , true } func ( *XXX_lazyUnmarshalInfo) ( []IndexEntry) { atomicStoreIndex(&.index, &) } // FindFieldInProto looks for field fieldNum in lazyUnmarshalInfo information // (including protobuf), returns startOffset/endOffset/found. func ( *XXX_lazyUnmarshalInfo) ( uint32) (, uint32, , bool, []IndexEntry) { if .Protobuf == nil { // There is no backing protobuf for this message -- it was made from a builder return 0, 0, false, false, nil } := atomicLoadIndex(&.index) if == nil { , := buildIndex(.Protobuf) if != nil { panic(fmt.Sprintf("findFieldInfo: error building index when looking for field %d: %v", , )) } // lazy.index is a pointer to the slice returned by BuildIndex = & atomicStoreIndex(&.index, ) } return lookupField(, ) } // 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 ( *[]IndexEntry, uint32) (, uint32, bool, bool, []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. := * for , := range { if == .FieldNum { if < len()-1 && .FieldNum == [+1].FieldNum { // Handle the uncommon case where there are // repeated entries for the same field which // are not contiguous in the protobuf. := make([]IndexEntry, 1, 2) [0] = IndexEntry{, .Start, .End, .MultipleContiguous} ++ for < len() && [].FieldNum == { = append(, IndexEntry{, [].Start, [].End, [].MultipleContiguous}) ++ } return 0, 0, false, false, } return .Start, .End, true, .MultipleContiguous, nil } if < .FieldNum { return 0, 0, false, false, nil } } return 0, 0, false, false, nil }