| // 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 | |
| } |