mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-11-04 07:22:25 -06:00 
			
		
		
		
	
		
			
	
	
		
			161 lines
		
	
	
	
		
			4.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
		
		
			
		
	
	
			161 lines
		
	
	
	
		
			4.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| 
								 | 
							
								// 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 (
							 | 
						||
| 
								 | 
							
									"bufio"
							 | 
						||
| 
								 | 
							
									"encoding/binary"
							 | 
						||
| 
								 | 
							
									"fmt"
							 | 
						||
| 
								 | 
							
									"io"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									"google.golang.org/protobuf/encoding/protowire"
							 | 
						||
| 
								 | 
							
									"google.golang.org/protobuf/internal/errors"
							 | 
						||
| 
								 | 
							
									"google.golang.org/protobuf/proto"
							 | 
						||
| 
								 | 
							
								)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// 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 (o MarshalOptions) MarshalTo(w io.Writer, m proto.Message) (int, error) {
							 | 
						||
| 
								 | 
							
									msgBytes, err := o.MarshalOptions.Marshal(m)
							 | 
						||
| 
								 | 
							
									if err != nil {
							 | 
						||
| 
								 | 
							
										return 0, err
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									sizeBytes := protowire.AppendVarint(nil, uint64(len(msgBytes)))
							 | 
						||
| 
								 | 
							
									sizeWritten, err := w.Write(sizeBytes)
							 | 
						||
| 
								 | 
							
									if err != nil {
							 | 
						||
| 
								 | 
							
										return sizeWritten, err
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									msgWritten, err := w.Write(msgBytes)
							 | 
						||
| 
								 | 
							
									if err != nil {
							 | 
						||
| 
								 | 
							
										return sizeWritten + msgWritten, err
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									return sizeWritten + msgWritten, nil
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// MarshalTo writes a varint size-delimited wire-format message to w
							 | 
						||
| 
								 | 
							
								// with the default options.
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								// See the documentation for [MarshalOptions.MarshalTo].
							 | 
						||
| 
								 | 
							
								func MarshalTo(w io.Writer, m proto.Message) (int, error) {
							 | 
						||
| 
								 | 
							
									return MarshalOptions{}.MarshalTo(w, m)
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// 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 (e *SizeTooLargeError) Error() string {
							 | 
						||
| 
								 | 
							
									return fmt.Sprintf("message size %d exceeded unmarshaler's maximum configured size %d", e.Size, e.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 (o UnmarshalOptions) UnmarshalFrom(r Reader, m proto.Message) error {
							 | 
						||
| 
								 | 
							
									var sizeArr [binary.MaxVarintLen64]byte
							 | 
						||
| 
								 | 
							
									sizeBuf := sizeArr[:0]
							 | 
						||
| 
								 | 
							
									for i := range sizeArr {
							 | 
						||
| 
								 | 
							
										b, err := r.ReadByte()
							 | 
						||
| 
								 | 
							
										if err != nil {
							 | 
						||
| 
								 | 
							
											// Immediate EOF is unexpected.
							 | 
						||
| 
								 | 
							
											if err == io.EOF && i != 0 {
							 | 
						||
| 
								 | 
							
												break
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											return err
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										sizeBuf = append(sizeBuf, b)
							 | 
						||
| 
								 | 
							
										if b < 0x80 {
							 | 
						||
| 
								 | 
							
											break
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									size, n := protowire.ConsumeVarint(sizeBuf)
							 | 
						||
| 
								 | 
							
									if n < 0 {
							 | 
						||
| 
								 | 
							
										return protowire.ParseError(n)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									maxSize := o.MaxSize
							 | 
						||
| 
								 | 
							
									if maxSize == 0 {
							 | 
						||
| 
								 | 
							
										maxSize = defaultMaxSize
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									if maxSize != -1 && size > uint64(maxSize) {
							 | 
						||
| 
								 | 
							
										return errors.Wrap(&SizeTooLargeError{Size: size, MaxSize: uint64(maxSize)}, "")
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									var b []byte
							 | 
						||
| 
								 | 
							
									var err error
							 | 
						||
| 
								 | 
							
									if br, ok := r.(*bufio.Reader); ok {
							 | 
						||
| 
								 | 
							
										// Use the []byte from the bufio.Reader instead of having to allocate one.
							 | 
						||
| 
								 | 
							
										// This reduces CPU usage and allocated bytes.
							 | 
						||
| 
								 | 
							
										b, err = br.Peek(int(size))
							 | 
						||
| 
								 | 
							
										if err == nil {
							 | 
						||
| 
								 | 
							
											defer br.Discard(int(size))
							 | 
						||
| 
								 | 
							
										} else {
							 | 
						||
| 
								 | 
							
											b = nil
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									if b == nil {
							 | 
						||
| 
								 | 
							
										b = make([]byte, size)
							 | 
						||
| 
								 | 
							
										_, err = io.ReadFull(r, b)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if err == io.EOF {
							 | 
						||
| 
								 | 
							
										return io.ErrUnexpectedEOF
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									if err != nil {
							 | 
						||
| 
								 | 
							
										return err
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									if err := o.Unmarshal(b, m); err != nil {
							 | 
						||
| 
								 | 
							
										return err
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									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 UnmarshalFrom(r Reader, m proto.Message) error {
							 | 
						||
| 
								 | 
							
									return UnmarshalOptions{}.UnmarshalFrom(r, m)
							 | 
						||
| 
								 | 
							
								}
							 |