1// Go support for Protocol Buffers - Google's data interchange format 2// 3// Copyright 2010 The Go Authors. All rights reserved. 4// https://github.com/golang/protobuf 5// 6// Redistribution and use in source and binary forms, with or without 7// modification, are permitted provided that the following conditions are 8// met: 9// 10// * Redistributions of source code must retain the above copyright 11// notice, this list of conditions and the following disclaimer. 12// * Redistributions in binary form must reproduce the above 13// copyright notice, this list of conditions and the following disclaimer 14// in the documentation and/or other materials provided with the 15// distribution. 16// * Neither the name of Google Inc. nor the names of its 17// contributors may be used to endorse or promote products derived from 18// this software without specific prior written permission. 19// 20// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 32package proto 33 34// Functions for writing the text protocol buffer format. 35 36import ( 37 "bufio" 38 "bytes" 39 "encoding" 40 "errors" 41 "fmt" 42 "io" 43 "log" 44 "math" 45 "reflect" 46 "sort" 47 "strings" 48) 49 50var ( 51 newline = []byte("\n") 52 spaces = []byte(" ") 53 endBraceNewline = []byte("}\n") 54 backslashN = []byte{'\\', 'n'} 55 backslashR = []byte{'\\', 'r'} 56 backslashT = []byte{'\\', 't'} 57 backslashDQ = []byte{'\\', '"'} 58 backslashBS = []byte{'\\', '\\'} 59 posInf = []byte("inf") 60 negInf = []byte("-inf") 61 nan = []byte("nan") 62) 63 64type writer interface { 65 io.Writer 66 WriteByte(byte) error 67} 68 69// textWriter is an io.Writer that tracks its indentation level. 70type textWriter struct { 71 ind int 72 complete bool // if the current position is a complete line 73 compact bool // whether to write out as a one-liner 74 w writer 75} 76 77func (w *textWriter) WriteString(s string) (n int, err error) { 78 if !strings.Contains(s, "\n") { 79 if !w.compact && w.complete { 80 w.writeIndent() 81 } 82 w.complete = false 83 return io.WriteString(w.w, s) 84 } 85 // WriteString is typically called without newlines, so this 86 // codepath and its copy are rare. We copy to avoid 87 // duplicating all of Write's logic here. 88 return w.Write([]byte(s)) 89} 90 91func (w *textWriter) Write(p []byte) (n int, err error) { 92 newlines := bytes.Count(p, newline) 93 if newlines == 0 { 94 if !w.compact && w.complete { 95 w.writeIndent() 96 } 97 n, err = w.w.Write(p) 98 w.complete = false 99 return n, err 100 } 101 102 frags := bytes.SplitN(p, newline, newlines+1) 103 if w.compact { 104 for i, frag := range frags { 105 if i > 0 { 106 if err := w.w.WriteByte(' '); err != nil { 107 return n, err 108 } 109 n++ 110 } 111 nn, err := w.w.Write(frag) 112 n += nn 113 if err != nil { 114 return n, err 115 } 116 } 117 return n, nil 118 } 119 120 for i, frag := range frags { 121 if w.complete { 122 w.writeIndent() 123 } 124 nn, err := w.w.Write(frag) 125 n += nn 126 if err != nil { 127 return n, err 128 } 129 if i+1 < len(frags) { 130 if err := w.w.WriteByte('\n'); err != nil { 131 return n, err 132 } 133 n++ 134 } 135 } 136 w.complete = len(frags[len(frags)-1]) == 0 137 return n, nil 138} 139 140func (w *textWriter) WriteByte(c byte) error { 141 if w.compact && c == '\n' { 142 c = ' ' 143 } 144 if !w.compact && w.complete { 145 w.writeIndent() 146 } 147 err := w.w.WriteByte(c) 148 w.complete = c == '\n' 149 return err 150} 151 152func (w *textWriter) indent() { w.ind++ } 153 154func (w *textWriter) unindent() { 155 if w.ind == 0 { 156 log.Print("proto: textWriter unindented too far") 157 return 158 } 159 w.ind-- 160} 161 162func writeName(w *textWriter, props *Properties) error { 163 if _, err := w.WriteString(props.OrigName); err != nil { 164 return err 165 } 166 if props.Wire != "group" { 167 return w.WriteByte(':') 168 } 169 return nil 170} 171 172func requiresQuotes(u string) bool { 173 // When type URL contains any characters except [0-9A-Za-z./\-]*, it must be quoted. 174 for _, ch := range u { 175 switch { 176 case ch == '.' || ch == '/' || ch == '_': 177 continue 178 case '0' <= ch && ch <= '9': 179 continue 180 case 'A' <= ch && ch <= 'Z': 181 continue 182 case 'a' <= ch && ch <= 'z': 183 continue 184 default: 185 return true 186 } 187 } 188 return false 189} 190 191// isAny reports whether sv is a google.protobuf.Any message 192func isAny(sv reflect.Value) bool { 193 type wkt interface { 194 XXX_WellKnownType() string 195 } 196 t, ok := sv.Addr().Interface().(wkt) 197 return ok && t.XXX_WellKnownType() == "Any" 198} 199 200// writeProto3Any writes an expanded google.protobuf.Any message. 201// 202// It returns (false, nil) if sv value can't be unmarshaled (e.g. because 203// required messages are not linked in). 204// 205// It returns (true, error) when sv was written in expanded format or an error 206// was encountered. 207func (tm *TextMarshaler) writeProto3Any(w *textWriter, sv reflect.Value) (bool, error) { 208 turl := sv.FieldByName("TypeUrl") 209 val := sv.FieldByName("Value") 210 if !turl.IsValid() || !val.IsValid() { 211 return true, errors.New("proto: invalid google.protobuf.Any message") 212 } 213 214 b, ok := val.Interface().([]byte) 215 if !ok { 216 return true, errors.New("proto: invalid google.protobuf.Any message") 217 } 218 219 parts := strings.Split(turl.String(), "/") 220 mt := MessageType(parts[len(parts)-1]) 221 if mt == nil { 222 return false, nil 223 } 224 m := reflect.New(mt.Elem()) 225 if err := Unmarshal(b, m.Interface().(Message)); err != nil { 226 return false, nil 227 } 228 w.Write([]byte("[")) 229 u := turl.String() 230 if requiresQuotes(u) { 231 writeString(w, u) 232 } else { 233 w.Write([]byte(u)) 234 } 235 if w.compact { 236 w.Write([]byte("]:<")) 237 } else { 238 w.Write([]byte("]: <\n")) 239 w.ind++ 240 } 241 if err := tm.writeStruct(w, m.Elem()); err != nil { 242 return true, err 243 } 244 if w.compact { 245 w.Write([]byte("> ")) 246 } else { 247 w.ind-- 248 w.Write([]byte(">\n")) 249 } 250 return true, nil 251} 252 253func (tm *TextMarshaler) writeStruct(w *textWriter, sv reflect.Value) error { 254 if tm.ExpandAny && isAny(sv) { 255 if canExpand, err := tm.writeProto3Any(w, sv); canExpand { 256 return err 257 } 258 } 259 st := sv.Type() 260 sprops := GetProperties(st) 261 for i := 0; i < sv.NumField(); i++ { 262 fv := sv.Field(i) 263 props := sprops.Prop[i] 264 name := st.Field(i).Name 265 266 if name == "XXX_NoUnkeyedLiteral" { 267 continue 268 } 269 270 if strings.HasPrefix(name, "XXX_") { 271 // There are two XXX_ fields: 272 // XXX_unrecognized []byte 273 // XXX_extensions map[int32]proto.Extension 274 // The first is handled here; 275 // the second is handled at the bottom of this function. 276 if name == "XXX_unrecognized" && !fv.IsNil() { 277 if err := writeUnknownStruct(w, fv.Interface().([]byte)); err != nil { 278 return err 279 } 280 } 281 continue 282 } 283 if fv.Kind() == reflect.Ptr && fv.IsNil() { 284 // Field not filled in. This could be an optional field or 285 // a required field that wasn't filled in. Either way, there 286 // isn't anything we can show for it. 287 continue 288 } 289 if fv.Kind() == reflect.Slice && fv.IsNil() { 290 // Repeated field that is empty, or a bytes field that is unused. 291 continue 292 } 293 294 if props.Repeated && fv.Kind() == reflect.Slice { 295 // Repeated field. 296 for j := 0; j < fv.Len(); j++ { 297 if err := writeName(w, props); err != nil { 298 return err 299 } 300 if !w.compact { 301 if err := w.WriteByte(' '); err != nil { 302 return err 303 } 304 } 305 v := fv.Index(j) 306 if v.Kind() == reflect.Ptr && v.IsNil() { 307 // A nil message in a repeated field is not valid, 308 // but we can handle that more gracefully than panicking. 309 if _, err := w.Write([]byte("<nil>\n")); err != nil { 310 return err 311 } 312 continue 313 } 314 if err := tm.writeAny(w, v, props); err != nil { 315 return err 316 } 317 if err := w.WriteByte('\n'); err != nil { 318 return err 319 } 320 } 321 continue 322 } 323 if fv.Kind() == reflect.Map { 324 // Map fields are rendered as a repeated struct with key/value fields. 325 keys := fv.MapKeys() 326 sort.Sort(mapKeys(keys)) 327 for _, key := range keys { 328 val := fv.MapIndex(key) 329 if err := writeName(w, props); err != nil { 330 return err 331 } 332 if !w.compact { 333 if err := w.WriteByte(' '); err != nil { 334 return err 335 } 336 } 337 // open struct 338 if err := w.WriteByte('<'); err != nil { 339 return err 340 } 341 if !w.compact { 342 if err := w.WriteByte('\n'); err != nil { 343 return err 344 } 345 } 346 w.indent() 347 // key 348 if _, err := w.WriteString("key:"); err != nil { 349 return err 350 } 351 if !w.compact { 352 if err := w.WriteByte(' '); err != nil { 353 return err 354 } 355 } 356 if err := tm.writeAny(w, key, props.MapKeyProp); err != nil { 357 return err 358 } 359 if err := w.WriteByte('\n'); err != nil { 360 return err 361 } 362 // nil values aren't legal, but we can avoid panicking because of them. 363 if val.Kind() != reflect.Ptr || !val.IsNil() { 364 // value 365 if _, err := w.WriteString("value:"); err != nil { 366 return err 367 } 368 if !w.compact { 369 if err := w.WriteByte(' '); err != nil { 370 return err 371 } 372 } 373 if err := tm.writeAny(w, val, props.MapValProp); err != nil { 374 return err 375 } 376 if err := w.WriteByte('\n'); err != nil { 377 return err 378 } 379 } 380 // close struct 381 w.unindent() 382 if err := w.WriteByte('>'); err != nil { 383 return err 384 } 385 if err := w.WriteByte('\n'); err != nil { 386 return err 387 } 388 } 389 continue 390 } 391 if props.proto3 && fv.Kind() == reflect.Slice && fv.Len() == 0 { 392 // empty bytes field 393 continue 394 } 395 if fv.Kind() != reflect.Ptr && fv.Kind() != reflect.Slice { 396 // proto3 non-repeated scalar field; skip if zero value 397 if isProto3Zero(fv) { 398 continue 399 } 400 } 401 402 if fv.Kind() == reflect.Interface { 403 // Check if it is a oneof. 404 if st.Field(i).Tag.Get("protobuf_oneof") != "" { 405 // fv is nil, or holds a pointer to generated struct. 406 // That generated struct has exactly one field, 407 // which has a protobuf struct tag. 408 if fv.IsNil() { 409 continue 410 } 411 inner := fv.Elem().Elem() // interface -> *T -> T 412 tag := inner.Type().Field(0).Tag.Get("protobuf") 413 props = new(Properties) // Overwrite the outer props var, but not its pointee. 414 props.Parse(tag) 415 // Write the value in the oneof, not the oneof itself. 416 fv = inner.Field(0) 417 418 // Special case to cope with malformed messages gracefully: 419 // If the value in the oneof is a nil pointer, don't panic 420 // in writeAny. 421 if fv.Kind() == reflect.Ptr && fv.IsNil() { 422 // Use errors.New so writeAny won't render quotes. 423 msg := errors.New("/* nil */") 424 fv = reflect.ValueOf(&msg).Elem() 425 } 426 } 427 } 428 429 if err := writeName(w, props); err != nil { 430 return err 431 } 432 if !w.compact { 433 if err := w.WriteByte(' '); err != nil { 434 return err 435 } 436 } 437 438 // Enums have a String method, so writeAny will work fine. 439 if err := tm.writeAny(w, fv, props); err != nil { 440 return err 441 } 442 443 if err := w.WriteByte('\n'); err != nil { 444 return err 445 } 446 } 447 448 // Extensions (the XXX_extensions field). 449 pv := sv.Addr() 450 if _, err := extendable(pv.Interface()); err == nil { 451 if err := tm.writeExtensions(w, pv); err != nil { 452 return err 453 } 454 } 455 456 return nil 457} 458 459var textMarshalerType = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem() 460 461// writeAny writes an arbitrary field. 462func (tm *TextMarshaler) writeAny(w *textWriter, v reflect.Value, props *Properties) error { 463 v = reflect.Indirect(v) 464 465 // Floats have special cases. 466 if v.Kind() == reflect.Float32 || v.Kind() == reflect.Float64 { 467 x := v.Float() 468 var b []byte 469 switch { 470 case math.IsInf(x, 1): 471 b = posInf 472 case math.IsInf(x, -1): 473 b = negInf 474 case math.IsNaN(x): 475 b = nan 476 } 477 if b != nil { 478 _, err := w.Write(b) 479 return err 480 } 481 // Other values are handled below. 482 } 483 484 // We don't attempt to serialise every possible value type; only those 485 // that can occur in protocol buffers. 486 switch v.Kind() { 487 case reflect.Slice: 488 // Should only be a []byte; repeated fields are handled in writeStruct. 489 if err := writeString(w, string(v.Bytes())); err != nil { 490 return err 491 } 492 case reflect.String: 493 if err := writeString(w, v.String()); err != nil { 494 return err 495 } 496 case reflect.Struct: 497 // Required/optional group/message. 498 var bra, ket byte = '<', '>' 499 if props != nil && props.Wire == "group" { 500 bra, ket = '{', '}' 501 } 502 if err := w.WriteByte(bra); err != nil { 503 return err 504 } 505 if !w.compact { 506 if err := w.WriteByte('\n'); err != nil { 507 return err 508 } 509 } 510 w.indent() 511 if v.CanAddr() { 512 // Calling v.Interface on a struct causes the reflect package to 513 // copy the entire struct. This is racy with the new Marshaler 514 // since we atomically update the XXX_sizecache. 515 // 516 // Thus, we retrieve a pointer to the struct if possible to avoid 517 // a race since v.Interface on the pointer doesn't copy the struct. 518 // 519 // If v is not addressable, then we are not worried about a race 520 // since it implies that the binary Marshaler cannot possibly be 521 // mutating this value. 522 v = v.Addr() 523 } 524 if v.Type().Implements(textMarshalerType) { 525 text, err := v.Interface().(encoding.TextMarshaler).MarshalText() 526 if err != nil { 527 return err 528 } 529 if _, err = w.Write(text); err != nil { 530 return err 531 } 532 } else { 533 if v.Kind() == reflect.Ptr { 534 v = v.Elem() 535 } 536 if err := tm.writeStruct(w, v); err != nil { 537 return err 538 } 539 } 540 w.unindent() 541 if err := w.WriteByte(ket); err != nil { 542 return err 543 } 544 default: 545 _, err := fmt.Fprint(w, v.Interface()) 546 return err 547 } 548 return nil 549} 550 551// equivalent to C's isprint. 552func isprint(c byte) bool { 553 return c >= 0x20 && c < 0x7f 554} 555 556// writeString writes a string in the protocol buffer text format. 557// It is similar to strconv.Quote except we don't use Go escape sequences, 558// we treat the string as a byte sequence, and we use octal escapes. 559// These differences are to maintain interoperability with the other 560// languages' implementations of the text format. 561func writeString(w *textWriter, s string) error { 562 // use WriteByte here to get any needed indent 563 if err := w.WriteByte('"'); err != nil { 564 return err 565 } 566 // Loop over the bytes, not the runes. 567 for i := 0; i < len(s); i++ { 568 var err error 569 // Divergence from C++: we don't escape apostrophes. 570 // There's no need to escape them, and the C++ parser 571 // copes with a naked apostrophe. 572 switch c := s[i]; c { 573 case '\n': 574 _, err = w.w.Write(backslashN) 575 case '\r': 576 _, err = w.w.Write(backslashR) 577 case '\t': 578 _, err = w.w.Write(backslashT) 579 case '"': 580 _, err = w.w.Write(backslashDQ) 581 case '\\': 582 _, err = w.w.Write(backslashBS) 583 default: 584 if isprint(c) { 585 err = w.w.WriteByte(c) 586 } else { 587 _, err = fmt.Fprintf(w.w, "\\%03o", c) 588 } 589 } 590 if err != nil { 591 return err 592 } 593 } 594 return w.WriteByte('"') 595} 596 597func writeUnknownStruct(w *textWriter, data []byte) (err error) { 598 if !w.compact { 599 if _, err := fmt.Fprintf(w, "/* %d unknown bytes */\n", len(data)); err != nil { 600 return err 601 } 602 } 603 b := NewBuffer(data) 604 for b.index < len(b.buf) { 605 x, err := b.DecodeVarint() 606 if err != nil { 607 _, err := fmt.Fprintf(w, "/* %v */\n", err) 608 return err 609 } 610 wire, tag := x&7, x>>3 611 if wire == WireEndGroup { 612 w.unindent() 613 if _, err := w.Write(endBraceNewline); err != nil { 614 return err 615 } 616 continue 617 } 618 if _, err := fmt.Fprint(w, tag); err != nil { 619 return err 620 } 621 if wire != WireStartGroup { 622 if err := w.WriteByte(':'); err != nil { 623 return err 624 } 625 } 626 if !w.compact || wire == WireStartGroup { 627 if err := w.WriteByte(' '); err != nil { 628 return err 629 } 630 } 631 switch wire { 632 case WireBytes: 633 buf, e := b.DecodeRawBytes(false) 634 if e == nil { 635 _, err = fmt.Fprintf(w, "%q", buf) 636 } else { 637 _, err = fmt.Fprintf(w, "/* %v */", e) 638 } 639 case WireFixed32: 640 x, err = b.DecodeFixed32() 641 err = writeUnknownInt(w, x, err) 642 case WireFixed64: 643 x, err = b.DecodeFixed64() 644 err = writeUnknownInt(w, x, err) 645 case WireStartGroup: 646 err = w.WriteByte('{') 647 w.indent() 648 case WireVarint: 649 x, err = b.DecodeVarint() 650 err = writeUnknownInt(w, x, err) 651 default: 652 _, err = fmt.Fprintf(w, "/* unknown wire type %d */", wire) 653 } 654 if err != nil { 655 return err 656 } 657 if err = w.WriteByte('\n'); err != nil { 658 return err 659 } 660 } 661 return nil 662} 663 664func writeUnknownInt(w *textWriter, x uint64, err error) error { 665 if err == nil { 666 _, err = fmt.Fprint(w, x) 667 } else { 668 _, err = fmt.Fprintf(w, "/* %v */", err) 669 } 670 return err 671} 672 673type int32Slice []int32 674 675func (s int32Slice) Len() int { return len(s) } 676func (s int32Slice) Less(i, j int) bool { return s[i] < s[j] } 677func (s int32Slice) Swap(i, j int) { s[i], s[j] = s[j], s[i] } 678 679// writeExtensions writes all the extensions in pv. 680// pv is assumed to be a pointer to a protocol message struct that is extendable. 681func (tm *TextMarshaler) writeExtensions(w *textWriter, pv reflect.Value) error { 682 emap := extensionMaps[pv.Type().Elem()] 683 ep, _ := extendable(pv.Interface()) 684 685 // Order the extensions by ID. 686 // This isn't strictly necessary, but it will give us 687 // canonical output, which will also make testing easier. 688 m, mu := ep.extensionsRead() 689 if m == nil { 690 return nil 691 } 692 mu.Lock() 693 ids := make([]int32, 0, len(m)) 694 for id := range m { 695 ids = append(ids, id) 696 } 697 sort.Sort(int32Slice(ids)) 698 mu.Unlock() 699 700 for _, extNum := range ids { 701 ext := m[extNum] 702 var desc *ExtensionDesc 703 if emap != nil { 704 desc = emap[extNum] 705 } 706 if desc == nil { 707 // Unknown extension. 708 if err := writeUnknownStruct(w, ext.enc); err != nil { 709 return err 710 } 711 continue 712 } 713 714 pb, err := GetExtension(ep, desc) 715 if err != nil { 716 return fmt.Errorf("failed getting extension: %v", err) 717 } 718 719 // Repeated extensions will appear as a slice. 720 if !desc.repeated() { 721 if err := tm.writeExtension(w, desc.Name, pb); err != nil { 722 return err 723 } 724 } else { 725 v := reflect.ValueOf(pb) 726 for i := 0; i < v.Len(); i++ { 727 if err := tm.writeExtension(w, desc.Name, v.Index(i).Interface()); err != nil { 728 return err 729 } 730 } 731 } 732 } 733 return nil 734} 735 736func (tm *TextMarshaler) writeExtension(w *textWriter, name string, pb interface{}) error { 737 if _, err := fmt.Fprintf(w, "[%s]:", name); err != nil { 738 return err 739 } 740 if !w.compact { 741 if err := w.WriteByte(' '); err != nil { 742 return err 743 } 744 } 745 if err := tm.writeAny(w, reflect.ValueOf(pb), nil); err != nil { 746 return err 747 } 748 if err := w.WriteByte('\n'); err != nil { 749 return err 750 } 751 return nil 752} 753 754func (w *textWriter) writeIndent() { 755 if !w.complete { 756 return 757 } 758 remain := w.ind * 2 759 for remain > 0 { 760 n := remain 761 if n > len(spaces) { 762 n = len(spaces) 763 } 764 w.w.Write(spaces[:n]) 765 remain -= n 766 } 767 w.complete = false 768} 769 770// TextMarshaler is a configurable text format marshaler. 771type TextMarshaler struct { 772 Compact bool // use compact text format (one line). 773 ExpandAny bool // expand google.protobuf.Any messages of known types 774} 775 776// Marshal writes a given protocol buffer in text format. 777// The only errors returned are from w. 778func (tm *TextMarshaler) Marshal(w io.Writer, pb Message) error { 779 val := reflect.ValueOf(pb) 780 if pb == nil || val.IsNil() { 781 w.Write([]byte("<nil>")) 782 return nil 783 } 784 var bw *bufio.Writer 785 ww, ok := w.(writer) 786 if !ok { 787 bw = bufio.NewWriter(w) 788 ww = bw 789 } 790 aw := &textWriter{ 791 w: ww, 792 complete: true, 793 compact: tm.Compact, 794 } 795 796 if etm, ok := pb.(encoding.TextMarshaler); ok { 797 text, err := etm.MarshalText() 798 if err != nil { 799 return err 800 } 801 if _, err = aw.Write(text); err != nil { 802 return err 803 } 804 if bw != nil { 805 return bw.Flush() 806 } 807 return nil 808 } 809 // Dereference the received pointer so we don't have outer < and >. 810 v := reflect.Indirect(val) 811 if err := tm.writeStruct(aw, v); err != nil { 812 return err 813 } 814 if bw != nil { 815 return bw.Flush() 816 } 817 return nil 818} 819 820// Text is the same as Marshal, but returns the string directly. 821func (tm *TextMarshaler) Text(pb Message) string { 822 var buf bytes.Buffer 823 tm.Marshal(&buf, pb) 824 return buf.String() 825} 826 827var ( 828 defaultTextMarshaler = TextMarshaler{} 829 compactTextMarshaler = TextMarshaler{Compact: true} 830) 831 832// TODO: consider removing some of the Marshal functions below. 833 834// MarshalText writes a given protocol buffer in text format. 835// The only errors returned are from w. 836func MarshalText(w io.Writer, pb Message) error { return defaultTextMarshaler.Marshal(w, pb) } 837 838// MarshalTextString is the same as MarshalText, but returns the string directly. 839func MarshalTextString(pb Message) string { return defaultTextMarshaler.Text(pb) } 840 841// CompactText writes a given protocol buffer in compact text format (one line). 842func CompactText(w io.Writer, pb Message) error { return compactTextMarshaler.Marshal(w, pb) } 843 844// CompactTextString is the same as CompactText, but returns the string directly. 845func CompactTextString(pb Message) string { return compactTextMarshaler.Text(pb) } 846