855 lines
21 KiB
Go
855 lines
21 KiB
Go
// Go support for Protocol Buffers - Google's data interchange format
|
|
//
|
|
// Copyright 2010 The Go Authors. All rights reserved.
|
|
// https://github.com/golang/protobuf
|
|
//
|
|
// Redistribution and use in source and binary forms, with or without
|
|
// modification, are permitted provided that the following conditions are
|
|
// met:
|
|
//
|
|
// * Redistributions of source code must retain the above copyright
|
|
// notice, this list of conditions and the following disclaimer.
|
|
// * Redistributions in binary form must reproduce the above
|
|
// copyright notice, this list of conditions and the following disclaimer
|
|
// in the documentation and/or other materials provided with the
|
|
// distribution.
|
|
// * Neither the name of Google Inc. nor the names of its
|
|
// contributors may be used to endorse or promote products derived from
|
|
// this software without specific prior written permission.
|
|
//
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
package proto
|
|
|
|
// Functions for writing the text protocol buffer format.
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"encoding"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"math"
|
|
"reflect"
|
|
"sort"
|
|
"strings"
|
|
)
|
|
|
|
var (
|
|
newline = []byte("\n")
|
|
spaces = []byte(" ")
|
|
gtNewline = []byte(">\n")
|
|
endBraceNewline = []byte("}\n")
|
|
backslashN = []byte{'\\', 'n'}
|
|
backslashR = []byte{'\\', 'r'}
|
|
backslashT = []byte{'\\', 't'}
|
|
backslashDQ = []byte{'\\', '"'}
|
|
backslashBS = []byte{'\\', '\\'}
|
|
posInf = []byte("inf")
|
|
negInf = []byte("-inf")
|
|
nan = []byte("nan")
|
|
)
|
|
|
|
type writer interface {
|
|
io.Writer
|
|
WriteByte(byte) error
|
|
}
|
|
|
|
// textWriter is an io.Writer that tracks its indentation level.
|
|
type textWriter struct {
|
|
ind int
|
|
complete bool // if the current position is a complete line
|
|
compact bool // whether to write out as a one-liner
|
|
w writer
|
|
}
|
|
|
|
func (w *textWriter) WriteString(s string) (n int, err error) {
|
|
if !strings.Contains(s, "\n") {
|
|
if !w.compact && w.complete {
|
|
w.writeIndent()
|
|
}
|
|
w.complete = false
|
|
return io.WriteString(w.w, s)
|
|
}
|
|
// WriteString is typically called without newlines, so this
|
|
// codepath and its copy are rare. We copy to avoid
|
|
// duplicating all of Write's logic here.
|
|
return w.Write([]byte(s))
|
|
}
|
|
|
|
func (w *textWriter) Write(p []byte) (n int, err error) {
|
|
newlines := bytes.Count(p, newline)
|
|
if newlines == 0 {
|
|
if !w.compact && w.complete {
|
|
w.writeIndent()
|
|
}
|
|
n, err = w.w.Write(p)
|
|
w.complete = false
|
|
return n, err
|
|
}
|
|
|
|
frags := bytes.SplitN(p, newline, newlines+1)
|
|
if w.compact {
|
|
for i, frag := range frags {
|
|
if i > 0 {
|
|
if err := w.w.WriteByte(' '); err != nil {
|
|
return n, err
|
|
}
|
|
n++
|
|
}
|
|
nn, err := w.w.Write(frag)
|
|
n += nn
|
|
if err != nil {
|
|
return n, err
|
|
}
|
|
}
|
|
return n, nil
|
|
}
|
|
|
|
for i, frag := range frags {
|
|
if w.complete {
|
|
w.writeIndent()
|
|
}
|
|
nn, err := w.w.Write(frag)
|
|
n += nn
|
|
if err != nil {
|
|
return n, err
|
|
}
|
|
if i+1 < len(frags) {
|
|
if err := w.w.WriteByte('\n'); err != nil {
|
|
return n, err
|
|
}
|
|
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()
|
|
}
|
|
err := w.w.WriteByte(c)
|
|
w.complete = c == '\n'
|
|
return err
|
|
}
|
|
|
|
func (w *textWriter) indent() { w.ind++ }
|
|
|
|
func (w *textWriter) unindent() {
|
|
if w.ind == 0 {
|
|
log.Print("proto: textWriter unindented too far")
|
|
return
|
|
}
|
|
w.ind--
|
|
}
|
|
|
|
func writeName(w *textWriter, props *Properties) error {
|
|
if _, err := w.WriteString(props.OrigName); err != nil {
|
|
return err
|
|
}
|
|
if props.Wire != "group" {
|
|
return w.WriteByte(':')
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// raw is the interface satisfied by RawMessage.
|
|
type raw interface {
|
|
Bytes() []byte
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
// isAny reports whether sv is a google.protobuf.Any message
|
|
func isAny(sv reflect.Value) bool {
|
|
type wkt interface {
|
|
XXX_WellKnownType() string
|
|
}
|
|
t, ok := sv.Addr().Interface().(wkt)
|
|
return ok && t.XXX_WellKnownType() == "Any"
|
|
}
|
|
|
|
// 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 (tm *TextMarshaler) writeProto3Any(w *textWriter, sv reflect.Value) (bool, error) {
|
|
turl := sv.FieldByName("TypeUrl")
|
|
val := sv.FieldByName("Value")
|
|
if !turl.IsValid() || !val.IsValid() {
|
|
return true, errors.New("proto: invalid google.protobuf.Any message")
|
|
}
|
|
|
|
b, ok := val.Interface().([]byte)
|
|
if !ok {
|
|
return true, errors.New("proto: invalid google.protobuf.Any message")
|
|
}
|
|
|
|
parts := strings.Split(turl.String(), "/")
|
|
mt := MessageType(parts[len(parts)-1])
|
|
if mt == nil {
|
|
return false, nil
|
|
}
|
|
m := reflect.New(mt.Elem())
|
|
if err := Unmarshal(b, m.Interface().(Message)); err != nil {
|
|
return false, nil
|
|
}
|
|
w.Write([]byte("["))
|
|
u := turl.String()
|
|
if requiresQuotes(u) {
|
|
writeString(w, u)
|
|
} else {
|
|
w.Write([]byte(u))
|
|
}
|
|
if w.compact {
|
|
w.Write([]byte("]:<"))
|
|
} else {
|
|
w.Write([]byte("]: <\n"))
|
|
w.ind++
|
|
}
|
|
if err := tm.writeStruct(w, m.Elem()); err != nil {
|
|
return true, err
|
|
}
|
|
if w.compact {
|
|
w.Write([]byte("> "))
|
|
} else {
|
|
w.ind--
|
|
w.Write([]byte(">\n"))
|
|
}
|
|
return true, nil
|
|
}
|
|
|
|
func (tm *TextMarshaler) writeStruct(w *textWriter, sv reflect.Value) error {
|
|
if tm.ExpandAny && isAny(sv) {
|
|
if canExpand, err := tm.writeProto3Any(w, sv); canExpand {
|
|
return err
|
|
}
|
|
}
|
|
st := sv.Type()
|
|
sprops := GetProperties(st)
|
|
for i := 0; i < sv.NumField(); i++ {
|
|
fv := sv.Field(i)
|
|
props := sprops.Prop[i]
|
|
name := st.Field(i).Name
|
|
|
|
if strings.HasPrefix(name, "XXX_") {
|
|
// There are two XXX_ fields:
|
|
// XXX_unrecognized []byte
|
|
// XXX_extensions map[int32]proto.Extension
|
|
// The first is handled here;
|
|
// the second is handled at the bottom of this function.
|
|
if name == "XXX_unrecognized" && !fv.IsNil() {
|
|
if err := writeUnknownStruct(w, fv.Interface().([]byte)); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
continue
|
|
}
|
|
if fv.Kind() == reflect.Ptr && fv.IsNil() {
|
|
// Field not filled in. This could be an optional field or
|
|
// a required field that wasn't filled in. Either way, there
|
|
// isn't anything we can show for it.
|
|
continue
|
|
}
|
|
if fv.Kind() == reflect.Slice && fv.IsNil() {
|
|
// Repeated field that is empty, or a bytes field that is unused.
|
|
continue
|
|
}
|
|
|
|
if props.Repeated && fv.Kind() == reflect.Slice {
|
|
// Repeated field.
|
|
for j := 0; j < fv.Len(); j++ {
|
|
if err := writeName(w, props); err != nil {
|
|
return err
|
|
}
|
|
if !w.compact {
|
|
if err := w.WriteByte(' '); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
v := fv.Index(j)
|
|
if v.Kind() == reflect.Ptr && v.IsNil() {
|
|
// A nil message in a repeated field is not valid,
|
|
// but we can handle that more gracefully than panicking.
|
|
if _, err := w.Write([]byte("<nil>\n")); err != nil {
|
|
return err
|
|
}
|
|
continue
|
|
}
|
|
if err := tm.writeAny(w, v, props); err != nil {
|
|
return err
|
|
}
|
|
if err := w.WriteByte('\n'); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
continue
|
|
}
|
|
if fv.Kind() == reflect.Map {
|
|
// Map fields are rendered as a repeated struct with key/value fields.
|
|
keys := fv.MapKeys()
|
|
sort.Sort(mapKeys(keys))
|
|
for _, key := range keys {
|
|
val := fv.MapIndex(key)
|
|
if err := writeName(w, props); err != nil {
|
|
return err
|
|
}
|
|
if !w.compact {
|
|
if err := w.WriteByte(' '); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
// open struct
|
|
if err := w.WriteByte('<'); err != nil {
|
|
return err
|
|
}
|
|
if !w.compact {
|
|
if err := w.WriteByte('\n'); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
w.indent()
|
|
// key
|
|
if _, err := w.WriteString("key:"); err != nil {
|
|
return err
|
|
}
|
|
if !w.compact {
|
|
if err := w.WriteByte(' '); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if err := tm.writeAny(w, key, props.mkeyprop); err != nil {
|
|
return err
|
|
}
|
|
if err := w.WriteByte('\n'); err != nil {
|
|
return err
|
|
}
|
|
// nil values aren't legal, but we can avoid panicking because of them.
|
|
if val.Kind() != reflect.Ptr || !val.IsNil() {
|
|
// value
|
|
if _, err := w.WriteString("value:"); err != nil {
|
|
return err
|
|
}
|
|
if !w.compact {
|
|
if err := w.WriteByte(' '); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if err := tm.writeAny(w, val, props.mvalprop); err != nil {
|
|
return err
|
|
}
|
|
if err := w.WriteByte('\n'); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
// close struct
|
|
w.unindent()
|
|
if err := w.WriteByte('>'); err != nil {
|
|
return err
|
|
}
|
|
if err := w.WriteByte('\n'); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
continue
|
|
}
|
|
if props.proto3 && fv.Kind() == reflect.Slice && fv.Len() == 0 {
|
|
// empty bytes field
|
|
continue
|
|
}
|
|
if fv.Kind() != reflect.Ptr && fv.Kind() != reflect.Slice {
|
|
// proto3 non-repeated scalar field; skip if zero value
|
|
if isProto3Zero(fv) {
|
|
continue
|
|
}
|
|
}
|
|
|
|
if fv.Kind() == reflect.Interface {
|
|
// Check if it is a oneof.
|
|
if st.Field(i).Tag.Get("protobuf_oneof") != "" {
|
|
// fv is nil, or holds a pointer to generated struct.
|
|
// That generated struct has exactly one field,
|
|
// which has a protobuf struct tag.
|
|
if fv.IsNil() {
|
|
continue
|
|
}
|
|
inner := fv.Elem().Elem() // interface -> *T -> T
|
|
tag := inner.Type().Field(0).Tag.Get("protobuf")
|
|
props = new(Properties) // Overwrite the outer props var, but not its pointee.
|
|
props.Parse(tag)
|
|
// Write the value in the oneof, not the oneof itself.
|
|
fv = inner.Field(0)
|
|
|
|
// Special case to cope with malformed messages gracefully:
|
|
// If the value in the oneof is a nil pointer, don't panic
|
|
// in writeAny.
|
|
if fv.Kind() == reflect.Ptr && fv.IsNil() {
|
|
// Use errors.New so writeAny won't render quotes.
|
|
msg := errors.New("/* nil */")
|
|
fv = reflect.ValueOf(&msg).Elem()
|
|
}
|
|
}
|
|
}
|
|
|
|
if err := writeName(w, props); err != nil {
|
|
return err
|
|
}
|
|
if !w.compact {
|
|
if err := w.WriteByte(' '); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if b, ok := fv.Interface().(raw); ok {
|
|
if err := writeRaw(w, b.Bytes()); err != nil {
|
|
return err
|
|
}
|
|
continue
|
|
}
|
|
|
|
// Enums have a String method, so writeAny will work fine.
|
|
if err := tm.writeAny(w, fv, props); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := w.WriteByte('\n'); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// Extensions (the XXX_extensions field).
|
|
pv := sv.Addr()
|
|
if _, ok := extendable(pv.Interface()); ok {
|
|
if err := tm.writeExtensions(w, pv); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// writeRaw writes an uninterpreted raw message.
|
|
func writeRaw(w *textWriter, b []byte) error {
|
|
if err := w.WriteByte('<'); err != nil {
|
|
return err
|
|
}
|
|
if !w.compact {
|
|
if err := w.WriteByte('\n'); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
w.indent()
|
|
if err := writeUnknownStruct(w, b); err != nil {
|
|
return err
|
|
}
|
|
w.unindent()
|
|
if err := w.WriteByte('>'); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// writeAny writes an arbitrary field.
|
|
func (tm *TextMarshaler) writeAny(w *textWriter, v reflect.Value, props *Properties) error {
|
|
v = reflect.Indirect(v)
|
|
|
|
// Floats have special cases.
|
|
if v.Kind() == reflect.Float32 || v.Kind() == reflect.Float64 {
|
|
x := v.Float()
|
|
var b []byte
|
|
switch {
|
|
case math.IsInf(x, 1):
|
|
b = posInf
|
|
case math.IsInf(x, -1):
|
|
b = negInf
|
|
case math.IsNaN(x):
|
|
b = nan
|
|
}
|
|
if b != nil {
|
|
_, err := w.Write(b)
|
|
return err
|
|
}
|
|
// Other values are handled below.
|
|
}
|
|
|
|
// We don't attempt to serialise every possible value type; only those
|
|
// that can occur in protocol buffers.
|
|
switch v.Kind() {
|
|
case reflect.Slice:
|
|
// Should only be a []byte; repeated fields are handled in writeStruct.
|
|
if err := writeString(w, string(v.Bytes())); err != nil {
|
|
return err
|
|
}
|
|
case reflect.String:
|
|
if err := writeString(w, v.String()); err != nil {
|
|
return err
|
|
}
|
|
case reflect.Struct:
|
|
// Required/optional group/message.
|
|
var bra, ket byte = '<', '>'
|
|
if props != nil && props.Wire == "group" {
|
|
bra, ket = '{', '}'
|
|
}
|
|
if err := w.WriteByte(bra); err != nil {
|
|
return err
|
|
}
|
|
if !w.compact {
|
|
if err := w.WriteByte('\n'); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
w.indent()
|
|
if etm, ok := v.Interface().(encoding.TextMarshaler); ok {
|
|
text, err := etm.MarshalText()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if _, err = w.Write(text); err != nil {
|
|
return err
|
|
}
|
|
} else if err := tm.writeStruct(w, v); err != nil {
|
|
return err
|
|
}
|
|
w.unindent()
|
|
if err := w.WriteByte(ket); err != nil {
|
|
return err
|
|
}
|
|
default:
|
|
_, err := fmt.Fprint(w, v.Interface())
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// equivalent to C's isprint.
|
|
func isprint(c byte) bool {
|
|
return c >= 0x20 && c < 0x7f
|
|
}
|
|
|
|
// writeString writes a string in the protocol buffer text format.
|
|
// It is similar to strconv.Quote except we don't use Go escape sequences,
|
|
// we treat the string as a byte sequence, and we use octal escapes.
|
|
// These differences are to maintain interoperability with the other
|
|
// languages' implementations of the text format.
|
|
func writeString(w *textWriter, s string) error {
|
|
// use WriteByte here to get any needed indent
|
|
if err := w.WriteByte('"'); err != nil {
|
|
return err
|
|
}
|
|
// Loop over the bytes, not the runes.
|
|
for i := 0; i < len(s); i++ {
|
|
var err error
|
|
// Divergence from C++: we don't escape apostrophes.
|
|
// There's no need to escape them, and the C++ parser
|
|
// copes with a naked apostrophe.
|
|
switch c := s[i]; c {
|
|
case '\n':
|
|
_, err = w.w.Write(backslashN)
|
|
case '\r':
|
|
_, err = w.w.Write(backslashR)
|
|
case '\t':
|
|
_, err = w.w.Write(backslashT)
|
|
case '"':
|
|
_, err = w.w.Write(backslashDQ)
|
|
case '\\':
|
|
_, err = w.w.Write(backslashBS)
|
|
default:
|
|
if isprint(c) {
|
|
err = w.w.WriteByte(c)
|
|
} else {
|
|
_, err = fmt.Fprintf(w.w, "\\%03o", c)
|
|
}
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return w.WriteByte('"')
|
|
}
|
|
|
|
func writeUnknownStruct(w *textWriter, data []byte) (err error) {
|
|
if !w.compact {
|
|
if _, err := fmt.Fprintf(w, "/* %d unknown bytes */\n", len(data)); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
b := NewBuffer(data)
|
|
for b.index < len(b.buf) {
|
|
x, err := b.DecodeVarint()
|
|
if err != nil {
|
|
_, err := fmt.Fprintf(w, "/* %v */\n", err)
|
|
return err
|
|
}
|
|
wire, tag := x&7, x>>3
|
|
if wire == WireEndGroup {
|
|
w.unindent()
|
|
if _, err := w.Write(endBraceNewline); err != nil {
|
|
return err
|
|
}
|
|
continue
|
|
}
|
|
if _, err := fmt.Fprint(w, tag); err != nil {
|
|
return err
|
|
}
|
|
if wire != WireStartGroup {
|
|
if err := w.WriteByte(':'); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if !w.compact || wire == WireStartGroup {
|
|
if err := w.WriteByte(' '); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
switch wire {
|
|
case WireBytes:
|
|
buf, e := b.DecodeRawBytes(false)
|
|
if e == nil {
|
|
_, err = fmt.Fprintf(w, "%q", buf)
|
|
} else {
|
|
_, err = fmt.Fprintf(w, "/* %v */", e)
|
|
}
|
|
case WireFixed32:
|
|
x, err = b.DecodeFixed32()
|
|
err = writeUnknownInt(w, x, err)
|
|
case WireFixed64:
|
|
x, err = b.DecodeFixed64()
|
|
err = writeUnknownInt(w, x, err)
|
|
case WireStartGroup:
|
|
err = w.WriteByte('{')
|
|
w.indent()
|
|
case WireVarint:
|
|
x, err = b.DecodeVarint()
|
|
err = writeUnknownInt(w, x, err)
|
|
default:
|
|
_, err = fmt.Fprintf(w, "/* unknown wire type %d */", wire)
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err = w.WriteByte('\n'); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func writeUnknownInt(w *textWriter, x uint64, err error) error {
|
|
if err == nil {
|
|
_, err = fmt.Fprint(w, x)
|
|
} else {
|
|
_, err = fmt.Fprintf(w, "/* %v */", err)
|
|
}
|
|
return err
|
|
}
|
|
|
|
type int32Slice []int32
|
|
|
|
func (s int32Slice) Len() int { return len(s) }
|
|
func (s int32Slice) Less(i, j int) bool { return s[i] < s[j] }
|
|
func (s int32Slice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
|
|
|
// writeExtensions writes all the extensions in pv.
|
|
// pv is assumed to be a pointer to a protocol message struct that is extendable.
|
|
func (tm *TextMarshaler) writeExtensions(w *textWriter, pv reflect.Value) error {
|
|
emap := extensionMaps[pv.Type().Elem()]
|
|
ep, _ := extendable(pv.Interface())
|
|
|
|
// Order the extensions by ID.
|
|
// This isn't strictly necessary, but it will give us
|
|
// canonical output, which will also make testing easier.
|
|
m, mu := ep.extensionsRead()
|
|
if m == nil {
|
|
return nil
|
|
}
|
|
mu.Lock()
|
|
ids := make([]int32, 0, len(m))
|
|
for id := range m {
|
|
ids = append(ids, id)
|
|
}
|
|
sort.Sort(int32Slice(ids))
|
|
mu.Unlock()
|
|
|
|
for _, extNum := range ids {
|
|
ext := m[extNum]
|
|
var desc *ExtensionDesc
|
|
if emap != nil {
|
|
desc = emap[extNum]
|
|
}
|
|
if desc == nil {
|
|
// Unknown extension.
|
|
if err := writeUnknownStruct(w, ext.enc); err != nil {
|
|
return err
|
|
}
|
|
continue
|
|
}
|
|
|
|
pb, err := GetExtension(ep, desc)
|
|
if err != nil {
|
|
return fmt.Errorf("failed getting extension: %v", err)
|
|
}
|
|
|
|
// Repeated extensions will appear as a slice.
|
|
if !desc.repeated() {
|
|
if err := tm.writeExtension(w, desc.Name, pb); err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
v := reflect.ValueOf(pb)
|
|
for i := 0; i < v.Len(); i++ {
|
|
if err := tm.writeExtension(w, desc.Name, v.Index(i).Interface()); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (tm *TextMarshaler) writeExtension(w *textWriter, name string, pb interface{}) error {
|
|
if _, err := fmt.Fprintf(w, "[%s]:", name); err != nil {
|
|
return err
|
|
}
|
|
if !w.compact {
|
|
if err := w.WriteByte(' '); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if err := tm.writeAny(w, reflect.ValueOf(pb), nil); err != nil {
|
|
return err
|
|
}
|
|
if err := w.WriteByte('\n'); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (w *textWriter) writeIndent() {
|
|
if !w.complete {
|
|
return
|
|
}
|
|
remain := w.ind * 2
|
|
for remain > 0 {
|
|
n := remain
|
|
if n > len(spaces) {
|
|
n = len(spaces)
|
|
}
|
|
w.w.Write(spaces[:n])
|
|
remain -= n
|
|
}
|
|
w.complete = 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 a given protocol buffer in text format.
|
|
// The only errors returned are from w.
|
|
func (tm *TextMarshaler) Marshal(w io.Writer, pb Message) error {
|
|
val := reflect.ValueOf(pb)
|
|
if pb == nil || val.IsNil() {
|
|
w.Write([]byte("<nil>"))
|
|
return nil
|
|
}
|
|
var bw *bufio.Writer
|
|
ww, ok := w.(writer)
|
|
if !ok {
|
|
bw = bufio.NewWriter(w)
|
|
ww = bw
|
|
}
|
|
aw := &textWriter{
|
|
w: ww,
|
|
complete: true,
|
|
compact: tm.Compact,
|
|
}
|
|
|
|
if etm, ok := pb.(encoding.TextMarshaler); ok {
|
|
text, err := etm.MarshalText()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if _, err = aw.Write(text); err != nil {
|
|
return err
|
|
}
|
|
if bw != nil {
|
|
return bw.Flush()
|
|
}
|
|
return nil
|
|
}
|
|
// Dereference the received pointer so we don't have outer < and >.
|
|
v := reflect.Indirect(val)
|
|
if err := tm.writeStruct(aw, v); err != nil {
|
|
return err
|
|
}
|
|
if bw != nil {
|
|
return bw.Flush()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Text is the same as Marshal, but returns the string directly.
|
|
func (tm *TextMarshaler) Text(pb Message) string {
|
|
var buf bytes.Buffer
|
|
tm.Marshal(&buf, pb)
|
|
return buf.String()
|
|
}
|
|
|
|
var (
|
|
defaultTextMarshaler = TextMarshaler{}
|
|
compactTextMarshaler = TextMarshaler{Compact: true}
|
|
)
|
|
|
|
// TODO: consider removing some of the Marshal functions below.
|
|
|
|
// MarshalText writes a given protocol buffer in text format.
|
|
// The only errors returned are from w.
|
|
func MarshalText(w io.Writer, pb Message) error { return defaultTextMarshaler.Marshal(w, pb) }
|
|
|
|
// MarshalTextString is the same as MarshalText, but returns the string directly.
|
|
func MarshalTextString(pb Message) string { return defaultTextMarshaler.Text(pb) }
|
|
|
|
// CompactText writes a given protocol buffer in compact text format (one line).
|
|
func CompactText(w io.Writer, pb Message) error { return compactTextMarshaler.Marshal(w, pb) }
|
|
|
|
// CompactTextString is the same as CompactText, but returns the string directly.
|
|
func CompactTextString(pb Message) string { return compactTextMarshaler.Text(pb) }
|