// Copyright 2022 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 protodelim marshals and unmarshals varint size-delimited messages.
package protodelim import ( ) // MarshalOptions is a configurable varint size-delimited marshaler. type MarshalOptions struct{ proto.MarshalOptions } // MarshalTo writes a varint size-delimited wire-format message to w. // If w returns an error, MarshalTo returns it unchanged. func ( MarshalOptions) ( io.Writer, proto.Message) (int, error) { , := .MarshalOptions.Marshal() if != nil { return 0, } := protowire.AppendVarint(nil, uint64(len())) , := .Write() if != nil { return , } , := .Write() if != nil { return + , } return + , nil } // MarshalTo writes a varint size-delimited wire-format message to w // with the default options. // // See the documentation for [MarshalOptions.MarshalTo]. func ( io.Writer, proto.Message) (int, error) { return MarshalOptions{}.MarshalTo(, ) } // UnmarshalOptions is a configurable varint size-delimited unmarshaler. type UnmarshalOptions struct { proto.UnmarshalOptions // MaxSize is the maximum size in wire-format bytes of a single message. // Unmarshaling a message larger than MaxSize will return an error. // A zero MaxSize will default to 4 MiB. // Setting MaxSize to -1 disables the limit. MaxSize int64 } const defaultMaxSize = 4 << 20 // 4 MiB, corresponds to the default gRPC max request/response size // SizeTooLargeError is an error that is returned when the unmarshaler encounters a message size // that is larger than its configured [UnmarshalOptions.MaxSize]. type SizeTooLargeError struct { // Size is the varint size of the message encountered // that was larger than the provided MaxSize. Size uint64 // MaxSize is the MaxSize limit configured in UnmarshalOptions, which Size exceeded. MaxSize uint64 } func ( *SizeTooLargeError) () string { return fmt.Sprintf("message size %d exceeded unmarshaler's maximum configured size %d", .Size, .MaxSize) } // Reader is the interface expected by [UnmarshalFrom]. // It is implemented by *[bufio.Reader]. type Reader interface { io.Reader io.ByteReader } // UnmarshalFrom parses and consumes a varint size-delimited wire-format message // from r. // The provided message must be mutable (e.g., a non-nil pointer to a message). // // The error is [io.EOF] error only if no bytes are read. // If an EOF happens after reading some but not all the bytes, // UnmarshalFrom returns a non-io.EOF error. // In particular if r returns a non-io.EOF error, UnmarshalFrom returns it unchanged, // and if only a size is read with no subsequent message, [io.ErrUnexpectedEOF] is returned. func ( UnmarshalOptions) ( Reader, proto.Message) error { var [binary.MaxVarintLen64]byte := [:0] for := range { , := .ReadByte() if != nil { // Immediate EOF is unexpected. if == io.EOF && != 0 { break } return } = append(, ) if < 0x80 { break } } , := protowire.ConsumeVarint() if < 0 { return protowire.ParseError() } := .MaxSize if == 0 { = defaultMaxSize } if != -1 && > uint64() { return errors.Wrap(&SizeTooLargeError{Size: , MaxSize: uint64()}, "") } var []byte var error if , := .(*bufio.Reader); { // Use the []byte from the bufio.Reader instead of having to allocate one. // This reduces CPU usage and allocated bytes. , = .Peek(int()) if == nil { defer .Discard(int()) } else { = nil } } if == nil { = make([]byte, ) _, = io.ReadFull(, ) } if == io.EOF { return io.ErrUnexpectedEOF } if != nil { return } if := .Unmarshal(, ); != nil { return } return nil } // UnmarshalFrom parses and consumes a varint size-delimited wire-format message // from r with the default options. // The provided message must be mutable (e.g., a non-nil pointer to a message). // // See the documentation for [UnmarshalOptions.UnmarshalFrom]. func ( Reader, proto.Message) error { return UnmarshalOptions{}.UnmarshalFrom(, ) }