mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-10-31 10:22:25 -05:00 
			
		
		
		
	* start fixing up tests * fix up tests + automate with drone * fiddle with linting * messing about with drone.yml * some more fiddling * hmmm * add cache * add vendor directory * verbose * ci updates * update some little things * update sig
		
			
				
	
	
		
			560 lines
		
	
	
	
		
			13 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			560 lines
		
	
	
	
		
			13 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright 2010 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 proto
 | |
| 
 | |
| import (
 | |
| 	"bytes"
 | |
| 	"encoding"
 | |
| 	"fmt"
 | |
| 	"io"
 | |
| 	"math"
 | |
| 	"sort"
 | |
| 	"strings"
 | |
| 
 | |
| 	"google.golang.org/protobuf/encoding/prototext"
 | |
| 	"google.golang.org/protobuf/encoding/protowire"
 | |
| 	"google.golang.org/protobuf/proto"
 | |
| 	"google.golang.org/protobuf/reflect/protoreflect"
 | |
| 	"google.golang.org/protobuf/reflect/protoregistry"
 | |
| )
 | |
| 
 | |
| const wrapTextMarshalV2 = false
 | |
| 
 | |
| // TextMarshaler is a configurable text format marshaler.
 | |
| type TextMarshaler struct {
 | |
| 	Compact   bool // use compact text format (one line)
 | |
| 	ExpandAny bool // expand google.protobuf.Any messages of known types
 | |
| }
 | |
| 
 | |
| // Marshal writes the proto text format of m to w.
 | |
| func (tm *TextMarshaler) Marshal(w io.Writer, m Message) error {
 | |
| 	b, err := tm.marshal(m)
 | |
| 	if len(b) > 0 {
 | |
| 		if _, err := w.Write(b); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 	return err
 | |
| }
 | |
| 
 | |
| // Text returns a proto text formatted string of m.
 | |
| func (tm *TextMarshaler) Text(m Message) string {
 | |
| 	b, _ := tm.marshal(m)
 | |
| 	return string(b)
 | |
| }
 | |
| 
 | |
| func (tm *TextMarshaler) marshal(m Message) ([]byte, error) {
 | |
| 	mr := MessageReflect(m)
 | |
| 	if mr == nil || !mr.IsValid() {
 | |
| 		return []byte("<nil>"), nil
 | |
| 	}
 | |
| 
 | |
| 	if wrapTextMarshalV2 {
 | |
| 		if m, ok := m.(encoding.TextMarshaler); ok {
 | |
| 			return m.MarshalText()
 | |
| 		}
 | |
| 
 | |
| 		opts := prototext.MarshalOptions{
 | |
| 			AllowPartial: true,
 | |
| 			EmitUnknown:  true,
 | |
| 		}
 | |
| 		if !tm.Compact {
 | |
| 			opts.Indent = "  "
 | |
| 		}
 | |
| 		if !tm.ExpandAny {
 | |
| 			opts.Resolver = (*protoregistry.Types)(nil)
 | |
| 		}
 | |
| 		return opts.Marshal(mr.Interface())
 | |
| 	} else {
 | |
| 		w := &textWriter{
 | |
| 			compact:   tm.Compact,
 | |
| 			expandAny: tm.ExpandAny,
 | |
| 			complete:  true,
 | |
| 		}
 | |
| 
 | |
| 		if m, ok := m.(encoding.TextMarshaler); ok {
 | |
| 			b, err := m.MarshalText()
 | |
| 			if err != nil {
 | |
| 				return nil, err
 | |
| 			}
 | |
| 			w.Write(b)
 | |
| 			return w.buf, nil
 | |
| 		}
 | |
| 
 | |
| 		err := w.writeMessage(mr)
 | |
| 		return w.buf, err
 | |
| 	}
 | |
| }
 | |
| 
 | |
| var (
 | |
| 	defaultTextMarshaler = TextMarshaler{}
 | |
| 	compactTextMarshaler = TextMarshaler{Compact: true}
 | |
| )
 | |
| 
 | |
| // MarshalText writes the proto text format of m to w.
 | |
| func MarshalText(w io.Writer, m Message) error { return defaultTextMarshaler.Marshal(w, m) }
 | |
| 
 | |
| // MarshalTextString returns a proto text formatted string of m.
 | |
| func MarshalTextString(m Message) string { return defaultTextMarshaler.Text(m) }
 | |
| 
 | |
| // CompactText writes the compact proto text format of m to w.
 | |
| func CompactText(w io.Writer, m Message) error { return compactTextMarshaler.Marshal(w, m) }
 | |
| 
 | |
| // CompactTextString returns a compact proto text formatted string of m.
 | |
| func CompactTextString(m Message) string { return compactTextMarshaler.Text(m) }
 | |
| 
 | |
| var (
 | |
| 	newline         = []byte("\n")
 | |
| 	endBraceNewline = []byte("}\n")
 | |
| 	posInf          = []byte("inf")
 | |
| 	negInf          = []byte("-inf")
 | |
| 	nan             = []byte("nan")
 | |
| )
 | |
| 
 | |
| // textWriter is an io.Writer that tracks its indentation level.
 | |
| type textWriter struct {
 | |
| 	compact   bool // same as TextMarshaler.Compact
 | |
| 	expandAny bool // same as TextMarshaler.ExpandAny
 | |
| 	complete  bool // whether the current position is a complete line
 | |
| 	indent    int  // indentation level; never negative
 | |
| 	buf       []byte
 | |
| }
 | |
| 
 | |
| func (w *textWriter) Write(p []byte) (n int, _ error) {
 | |
| 	newlines := bytes.Count(p, newline)
 | |
| 	if newlines == 0 {
 | |
| 		if !w.compact && w.complete {
 | |
| 			w.writeIndent()
 | |
| 		}
 | |
| 		w.buf = append(w.buf, p...)
 | |
| 		w.complete = false
 | |
| 		return len(p), nil
 | |
| 	}
 | |
| 
 | |
| 	frags := bytes.SplitN(p, newline, newlines+1)
 | |
| 	if w.compact {
 | |
| 		for i, frag := range frags {
 | |
| 			if i > 0 {
 | |
| 				w.buf = append(w.buf, ' ')
 | |
| 				n++
 | |
| 			}
 | |
| 			w.buf = append(w.buf, frag...)
 | |
| 			n += len(frag)
 | |
| 		}
 | |
| 		return n, nil
 | |
| 	}
 | |
| 
 | |
| 	for i, frag := range frags {
 | |
| 		if w.complete {
 | |
| 			w.writeIndent()
 | |
| 		}
 | |
| 		w.buf = append(w.buf, frag...)
 | |
| 		n += len(frag)
 | |
| 		if i+1 < len(frags) {
 | |
| 			w.buf = append(w.buf, '\n')
 | |
| 			n++
 | |
| 		}
 | |
| 	}
 | |
| 	w.complete = len(frags[len(frags)-1]) == 0
 | |
| 	return n, nil
 | |
| }
 | |
| 
 | |
| func (w *textWriter) WriteByte(c byte) error {
 | |
| 	if w.compact && c == '\n' {
 | |
| 		c = ' '
 | |
| 	}
 | |
| 	if !w.compact && w.complete {
 | |
| 		w.writeIndent()
 | |
| 	}
 | |
| 	w.buf = append(w.buf, c)
 | |
| 	w.complete = c == '\n'
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (w *textWriter) writeName(fd protoreflect.FieldDescriptor) {
 | |
| 	if !w.compact && w.complete {
 | |
| 		w.writeIndent()
 | |
| 	}
 | |
| 	w.complete = false
 | |
| 
 | |
| 	if fd.Kind() != protoreflect.GroupKind {
 | |
| 		w.buf = append(w.buf, fd.Name()...)
 | |
| 		w.WriteByte(':')
 | |
| 	} else {
 | |
| 		// Use message type name for group field name.
 | |
| 		w.buf = append(w.buf, fd.Message().Name()...)
 | |
| 	}
 | |
| 
 | |
| 	if !w.compact {
 | |
| 		w.WriteByte(' ')
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func requiresQuotes(u string) bool {
 | |
| 	// When type URL contains any characters except [0-9A-Za-z./\-]*, it must be quoted.
 | |
| 	for _, ch := range u {
 | |
| 		switch {
 | |
| 		case ch == '.' || ch == '/' || ch == '_':
 | |
| 			continue
 | |
| 		case '0' <= ch && ch <= '9':
 | |
| 			continue
 | |
| 		case 'A' <= ch && ch <= 'Z':
 | |
| 			continue
 | |
| 		case 'a' <= ch && ch <= 'z':
 | |
| 			continue
 | |
| 		default:
 | |
| 			return true
 | |
| 		}
 | |
| 	}
 | |
| 	return false
 | |
| }
 | |
| 
 | |
| // writeProto3Any writes an expanded google.protobuf.Any message.
 | |
| //
 | |
| // It returns (false, nil) if sv value can't be unmarshaled (e.g. because
 | |
| // required messages are not linked in).
 | |
| //
 | |
| // It returns (true, error) when sv was written in expanded format or an error
 | |
| // was encountered.
 | |
| func (w *textWriter) writeProto3Any(m protoreflect.Message) (bool, error) {
 | |
| 	md := m.Descriptor()
 | |
| 	fdURL := md.Fields().ByName("type_url")
 | |
| 	fdVal := md.Fields().ByName("value")
 | |
| 
 | |
| 	url := m.Get(fdURL).String()
 | |
| 	mt, err := protoregistry.GlobalTypes.FindMessageByURL(url)
 | |
| 	if err != nil {
 | |
| 		return false, nil
 | |
| 	}
 | |
| 
 | |
| 	b := m.Get(fdVal).Bytes()
 | |
| 	m2 := mt.New()
 | |
| 	if err := proto.Unmarshal(b, m2.Interface()); err != nil {
 | |
| 		return false, nil
 | |
| 	}
 | |
| 	w.Write([]byte("["))
 | |
| 	if requiresQuotes(url) {
 | |
| 		w.writeQuotedString(url)
 | |
| 	} else {
 | |
| 		w.Write([]byte(url))
 | |
| 	}
 | |
| 	if w.compact {
 | |
| 		w.Write([]byte("]:<"))
 | |
| 	} else {
 | |
| 		w.Write([]byte("]: <\n"))
 | |
| 		w.indent++
 | |
| 	}
 | |
| 	if err := w.writeMessage(m2); err != nil {
 | |
| 		return true, err
 | |
| 	}
 | |
| 	if w.compact {
 | |
| 		w.Write([]byte("> "))
 | |
| 	} else {
 | |
| 		w.indent--
 | |
| 		w.Write([]byte(">\n"))
 | |
| 	}
 | |
| 	return true, nil
 | |
| }
 | |
| 
 | |
| func (w *textWriter) writeMessage(m protoreflect.Message) error {
 | |
| 	md := m.Descriptor()
 | |
| 	if w.expandAny && md.FullName() == "google.protobuf.Any" {
 | |
| 		if canExpand, err := w.writeProto3Any(m); canExpand {
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	fds := md.Fields()
 | |
| 	for i := 0; i < fds.Len(); {
 | |
| 		fd := fds.Get(i)
 | |
| 		if od := fd.ContainingOneof(); od != nil {
 | |
| 			fd = m.WhichOneof(od)
 | |
| 			i += od.Fields().Len()
 | |
| 		} else {
 | |
| 			i++
 | |
| 		}
 | |
| 		if fd == nil || !m.Has(fd) {
 | |
| 			continue
 | |
| 		}
 | |
| 
 | |
| 		switch {
 | |
| 		case fd.IsList():
 | |
| 			lv := m.Get(fd).List()
 | |
| 			for j := 0; j < lv.Len(); j++ {
 | |
| 				w.writeName(fd)
 | |
| 				v := lv.Get(j)
 | |
| 				if err := w.writeSingularValue(v, fd); err != nil {
 | |
| 					return err
 | |
| 				}
 | |
| 				w.WriteByte('\n')
 | |
| 			}
 | |
| 		case fd.IsMap():
 | |
| 			kfd := fd.MapKey()
 | |
| 			vfd := fd.MapValue()
 | |
| 			mv := m.Get(fd).Map()
 | |
| 
 | |
| 			type entry struct{ key, val protoreflect.Value }
 | |
| 			var entries []entry
 | |
| 			mv.Range(func(k protoreflect.MapKey, v protoreflect.Value) bool {
 | |
| 				entries = append(entries, entry{k.Value(), v})
 | |
| 				return true
 | |
| 			})
 | |
| 			sort.Slice(entries, func(i, j int) bool {
 | |
| 				switch kfd.Kind() {
 | |
| 				case protoreflect.BoolKind:
 | |
| 					return !entries[i].key.Bool() && entries[j].key.Bool()
 | |
| 				case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind, protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind:
 | |
| 					return entries[i].key.Int() < entries[j].key.Int()
 | |
| 				case protoreflect.Uint32Kind, protoreflect.Fixed32Kind, protoreflect.Uint64Kind, protoreflect.Fixed64Kind:
 | |
| 					return entries[i].key.Uint() < entries[j].key.Uint()
 | |
| 				case protoreflect.StringKind:
 | |
| 					return entries[i].key.String() < entries[j].key.String()
 | |
| 				default:
 | |
| 					panic("invalid kind")
 | |
| 				}
 | |
| 			})
 | |
| 			for _, entry := range entries {
 | |
| 				w.writeName(fd)
 | |
| 				w.WriteByte('<')
 | |
| 				if !w.compact {
 | |
| 					w.WriteByte('\n')
 | |
| 				}
 | |
| 				w.indent++
 | |
| 				w.writeName(kfd)
 | |
| 				if err := w.writeSingularValue(entry.key, kfd); err != nil {
 | |
| 					return err
 | |
| 				}
 | |
| 				w.WriteByte('\n')
 | |
| 				w.writeName(vfd)
 | |
| 				if err := w.writeSingularValue(entry.val, vfd); err != nil {
 | |
| 					return err
 | |
| 				}
 | |
| 				w.WriteByte('\n')
 | |
| 				w.indent--
 | |
| 				w.WriteByte('>')
 | |
| 				w.WriteByte('\n')
 | |
| 			}
 | |
| 		default:
 | |
| 			w.writeName(fd)
 | |
| 			if err := w.writeSingularValue(m.Get(fd), fd); err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 			w.WriteByte('\n')
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if b := m.GetUnknown(); len(b) > 0 {
 | |
| 		w.writeUnknownFields(b)
 | |
| 	}
 | |
| 	return w.writeExtensions(m)
 | |
| }
 | |
| 
 | |
| func (w *textWriter) writeSingularValue(v protoreflect.Value, fd protoreflect.FieldDescriptor) error {
 | |
| 	switch fd.Kind() {
 | |
| 	case protoreflect.FloatKind, protoreflect.DoubleKind:
 | |
| 		switch vf := v.Float(); {
 | |
| 		case math.IsInf(vf, +1):
 | |
| 			w.Write(posInf)
 | |
| 		case math.IsInf(vf, -1):
 | |
| 			w.Write(negInf)
 | |
| 		case math.IsNaN(vf):
 | |
| 			w.Write(nan)
 | |
| 		default:
 | |
| 			fmt.Fprint(w, v.Interface())
 | |
| 		}
 | |
| 	case protoreflect.StringKind:
 | |
| 		// NOTE: This does not validate UTF-8 for historical reasons.
 | |
| 		w.writeQuotedString(string(v.String()))
 | |
| 	case protoreflect.BytesKind:
 | |
| 		w.writeQuotedString(string(v.Bytes()))
 | |
| 	case protoreflect.MessageKind, protoreflect.GroupKind:
 | |
| 		var bra, ket byte = '<', '>'
 | |
| 		if fd.Kind() == protoreflect.GroupKind {
 | |
| 			bra, ket = '{', '}'
 | |
| 		}
 | |
| 		w.WriteByte(bra)
 | |
| 		if !w.compact {
 | |
| 			w.WriteByte('\n')
 | |
| 		}
 | |
| 		w.indent++
 | |
| 		m := v.Message()
 | |
| 		if m2, ok := m.Interface().(encoding.TextMarshaler); ok {
 | |
| 			b, err := m2.MarshalText()
 | |
| 			if err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 			w.Write(b)
 | |
| 		} else {
 | |
| 			w.writeMessage(m)
 | |
| 		}
 | |
| 		w.indent--
 | |
| 		w.WriteByte(ket)
 | |
| 	case protoreflect.EnumKind:
 | |
| 		if ev := fd.Enum().Values().ByNumber(v.Enum()); ev != nil {
 | |
| 			fmt.Fprint(w, ev.Name())
 | |
| 		} else {
 | |
| 			fmt.Fprint(w, v.Enum())
 | |
| 		}
 | |
| 	default:
 | |
| 		fmt.Fprint(w, v.Interface())
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // writeQuotedString writes a quoted string in the protocol buffer text format.
 | |
| func (w *textWriter) writeQuotedString(s string) {
 | |
| 	w.WriteByte('"')
 | |
| 	for i := 0; i < len(s); i++ {
 | |
| 		switch c := s[i]; c {
 | |
| 		case '\n':
 | |
| 			w.buf = append(w.buf, `\n`...)
 | |
| 		case '\r':
 | |
| 			w.buf = append(w.buf, `\r`...)
 | |
| 		case '\t':
 | |
| 			w.buf = append(w.buf, `\t`...)
 | |
| 		case '"':
 | |
| 			w.buf = append(w.buf, `\"`...)
 | |
| 		case '\\':
 | |
| 			w.buf = append(w.buf, `\\`...)
 | |
| 		default:
 | |
| 			if isPrint := c >= 0x20 && c < 0x7f; isPrint {
 | |
| 				w.buf = append(w.buf, c)
 | |
| 			} else {
 | |
| 				w.buf = append(w.buf, fmt.Sprintf(`\%03o`, c)...)
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	w.WriteByte('"')
 | |
| }
 | |
| 
 | |
| func (w *textWriter) writeUnknownFields(b []byte) {
 | |
| 	if !w.compact {
 | |
| 		fmt.Fprintf(w, "/* %d unknown bytes */\n", len(b))
 | |
| 	}
 | |
| 
 | |
| 	for len(b) > 0 {
 | |
| 		num, wtyp, n := protowire.ConsumeTag(b)
 | |
| 		if n < 0 {
 | |
| 			return
 | |
| 		}
 | |
| 		b = b[n:]
 | |
| 
 | |
| 		if wtyp == protowire.EndGroupType {
 | |
| 			w.indent--
 | |
| 			w.Write(endBraceNewline)
 | |
| 			continue
 | |
| 		}
 | |
| 		fmt.Fprint(w, num)
 | |
| 		if wtyp != protowire.StartGroupType {
 | |
| 			w.WriteByte(':')
 | |
| 		}
 | |
| 		if !w.compact || wtyp == protowire.StartGroupType {
 | |
| 			w.WriteByte(' ')
 | |
| 		}
 | |
| 		switch wtyp {
 | |
| 		case protowire.VarintType:
 | |
| 			v, n := protowire.ConsumeVarint(b)
 | |
| 			if n < 0 {
 | |
| 				return
 | |
| 			}
 | |
| 			b = b[n:]
 | |
| 			fmt.Fprint(w, v)
 | |
| 		case protowire.Fixed32Type:
 | |
| 			v, n := protowire.ConsumeFixed32(b)
 | |
| 			if n < 0 {
 | |
| 				return
 | |
| 			}
 | |
| 			b = b[n:]
 | |
| 			fmt.Fprint(w, v)
 | |
| 		case protowire.Fixed64Type:
 | |
| 			v, n := protowire.ConsumeFixed64(b)
 | |
| 			if n < 0 {
 | |
| 				return
 | |
| 			}
 | |
| 			b = b[n:]
 | |
| 			fmt.Fprint(w, v)
 | |
| 		case protowire.BytesType:
 | |
| 			v, n := protowire.ConsumeBytes(b)
 | |
| 			if n < 0 {
 | |
| 				return
 | |
| 			}
 | |
| 			b = b[n:]
 | |
| 			fmt.Fprintf(w, "%q", v)
 | |
| 		case protowire.StartGroupType:
 | |
| 			w.WriteByte('{')
 | |
| 			w.indent++
 | |
| 		default:
 | |
| 			fmt.Fprintf(w, "/* unknown wire type %d */", wtyp)
 | |
| 		}
 | |
| 		w.WriteByte('\n')
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // writeExtensions writes all the extensions in m.
 | |
| func (w *textWriter) writeExtensions(m protoreflect.Message) error {
 | |
| 	md := m.Descriptor()
 | |
| 	if md.ExtensionRanges().Len() == 0 {
 | |
| 		return nil
 | |
| 	}
 | |
| 
 | |
| 	type ext struct {
 | |
| 		desc protoreflect.FieldDescriptor
 | |
| 		val  protoreflect.Value
 | |
| 	}
 | |
| 	var exts []ext
 | |
| 	m.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool {
 | |
| 		if fd.IsExtension() {
 | |
| 			exts = append(exts, ext{fd, v})
 | |
| 		}
 | |
| 		return true
 | |
| 	})
 | |
| 	sort.Slice(exts, func(i, j int) bool {
 | |
| 		return exts[i].desc.Number() < exts[j].desc.Number()
 | |
| 	})
 | |
| 
 | |
| 	for _, ext := range exts {
 | |
| 		// For message set, use the name of the message as the extension name.
 | |
| 		name := string(ext.desc.FullName())
 | |
| 		if isMessageSet(ext.desc.ContainingMessage()) {
 | |
| 			name = strings.TrimSuffix(name, ".message_set_extension")
 | |
| 		}
 | |
| 
 | |
| 		if !ext.desc.IsList() {
 | |
| 			if err := w.writeSingularExtension(name, ext.val, ext.desc); err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 		} else {
 | |
| 			lv := ext.val.List()
 | |
| 			for i := 0; i < lv.Len(); i++ {
 | |
| 				if err := w.writeSingularExtension(name, lv.Get(i), ext.desc); err != nil {
 | |
| 					return err
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (w *textWriter) writeSingularExtension(name string, v protoreflect.Value, fd protoreflect.FieldDescriptor) error {
 | |
| 	fmt.Fprintf(w, "[%s]:", name)
 | |
| 	if !w.compact {
 | |
| 		w.WriteByte(' ')
 | |
| 	}
 | |
| 	if err := w.writeSingularValue(v, fd); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	w.WriteByte('\n')
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (w *textWriter) writeIndent() {
 | |
| 	if !w.complete {
 | |
| 		return
 | |
| 	}
 | |
| 	for i := 0; i < w.indent*2; i++ {
 | |
| 		w.buf = append(w.buf, ' ')
 | |
| 	}
 | |
| 	w.complete = false
 | |
| }
 |