mirror of
https://github.com/minio/minio.git
synced 2024-12-28 08:05:55 -05:00
2aa18cafc6
With CoreDNS now supporting etcdv3 as the DNS backend, we can update our federation target to etcdv3. Users will now be able to use etcdv3 server as the federation backbone. Minio will update bucket data to etcdv3 and CoreDNS can pick that data up and serve it as bucket style DNS path.
844 lines
21 KiB
Go
844 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(" ")
|
|
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
|
|
}
|
|
|
|
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 name == "XXX_NoUnkeyedLiteral" {
|
|
continue
|
|
}
|
|
|
|
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
|
|
}
|
|
}
|
|
|
|
// 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 _, err := extendable(pv.Interface()); err == nil {
|
|
if err := tm.writeExtensions(w, pv); 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 v.CanAddr() {
|
|
// Calling v.Interface on a struct causes the reflect package to
|
|
// copy the entire struct. This is racy with the new Marshaler
|
|
// since we atomically update the XXX_sizecache.
|
|
//
|
|
// Thus, we retrieve a pointer to the struct if possible to avoid
|
|
// a race since v.Interface on the pointer doesn't copy the struct.
|
|
//
|
|
// If v is not addressable, then we are not worried about a race
|
|
// since it implies that the binary Marshaler cannot possibly be
|
|
// mutating this value.
|
|
v = v.Addr()
|
|
}
|
|
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 v.Kind() == reflect.Ptr {
|
|
v = v.Elem()
|
|
}
|
|
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) }
|