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.mkeyprop); 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.mvalprop); 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 459// writeAny writes an arbitrary field. 460func (tm *TextMarshaler) writeAny(w *textWriter, v reflect.Value, props *Properties) error { 461 v = reflect.Indirect(v) 462 463 // Floats have special cases. 464 if v.Kind() == reflect.Float32 || v.Kind() == reflect.Float64 { 465 x := v.Float() 466 var b []byte 467 switch { 468 case math.IsInf(x, 1): 469 b = posInf 470 case math.IsInf(x, -1): 471 b = negInf 472 case math.IsNaN(x): 473 b = nan 474 } 475 if b != nil { 476 _, err := w.Write(b) 477 return err 478 } 479 // Other values are handled below. 480 } 481 482 // We don't attempt to serialise every possible value type; only those 483 // that can occur in protocol buffers. 484 switch v.Kind() { 485 case reflect.Slice: 486 // Should only be a []byte; repeated fields are handled in writeStruct. 487 if err := writeString(w, string(v.Bytes())); err != nil { 488 return err 489 } 490 case reflect.String: 491 if err := writeString(w, v.String()); err != nil { 492 return err 493 } 494 case reflect.Struct: 495 // Required/optional group/message. 496 var bra, ket byte = '<', '>' 497 if props != nil && props.Wire == "group" { 498 bra, ket = '{', '}' 499 } 500 if err := w.WriteByte(bra); err != nil { 501 return err 502 } 503 if !w.compact { 504 if err := w.WriteByte('\n'); err != nil { 505 return err 506 } 507 } 508 w.indent() 509 if v.CanAddr() { 510 // Calling v.Interface on a struct causes the reflect package to 511 // copy the entire struct. This is racy with the new Marshaler 512 // since we atomically update the XXX_sizecache. 513 // 514 // Thus, we retrieve a pointer to the struct if possible to avoid 515 // a race since v.Interface on the pointer doesn't copy the struct. 516 // 517 // If v is not addressable, then we are not worried about a race 518 // since it implies that the binary Marshaler cannot possibly be 519 // mutating this value. 520 v = v.Addr() 521 } 522 if etm, ok := v.Interface().(encoding.TextMarshaler); ok { 523 text, err := etm.MarshalText() 524 if err != nil { 525 return err 526 } 527 if _, err = w.Write(text); err != nil { 528 return err 529 } 530 } else { 531 if v.Kind() == reflect.Ptr { 532 v = v.Elem() 533 } 534 if err := tm.writeStruct(w, v); err != nil { 535 return err 536 } 537 } 538 w.unindent() 539 if err := w.WriteByte(ket); err != nil { 540 return err 541 } 542 default: 543 _, err := fmt.Fprint(w, v.Interface()) 544 return err 545 } 546 return nil 547} 548 549// equivalent to C's isprint. 550func isprint(c byte) bool { 551 return c >= 0x20 && c < 0x7f 552} 553 554// writeString writes a string in the protocol buffer text format. 555// It is similar to strconv.Quote except we don't use Go escape sequences, 556// we treat the string as a byte sequence, and we use octal escapes. 557// These differences are to maintain interoperability with the other 558// languages' implementations of the text format. 559func writeString(w *textWriter, s string) error { 560 // use WriteByte here to get any needed indent 561 if err := w.WriteByte('"'); err != nil { 562 return err 563 } 564 // Loop over the bytes, not the runes. 565 for i := 0; i < len(s); i++ { 566 var err error 567 // Divergence from C++: we don't escape apostrophes. 568 // There's no need to escape them, and the C++ parser 569 // copes with a naked apostrophe. 570 switch c := s[i]; c { 571 case '\n': 572 _, err = w.w.Write(backslashN) 573 case '\r': 574 _, err = w.w.Write(backslashR) 575 case '\t': 576 _, err = w.w.Write(backslashT) 577 case '"': 578 _, err = w.w.Write(backslashDQ) 579 case '\\': 580 _, err = w.w.Write(backslashBS) 581 default: 582 if isprint(c) { 583 err = w.w.WriteByte(c) 584 } else { 585 _, err = fmt.Fprintf(w.w, "\\%03o", c) 586 } 587 } 588 if err != nil { 589 return err 590 } 591 } 592 return w.WriteByte('"') 593} 594 595func writeUnknownStruct(w *textWriter, data []byte) (err error) { 596 if !w.compact { 597 if _, err := fmt.Fprintf(w, "/* %d unknown bytes */\n", len(data)); err != nil { 598 return err 599 } 600 } 601 b := NewBuffer(data) 602 for b.index < len(b.buf) { 603 x, err := b.DecodeVarint() 604 if err != nil { 605 _, err := fmt.Fprintf(w, "/* %v */\n", err) 606 return err 607 } 608 wire, tag := x&7, x>>3 609 if wire == WireEndGroup { 610 w.unindent() 611 if _, err := w.Write(endBraceNewline); err != nil { 612 return err 613 } 614 continue 615 } 616 if _, err := fmt.Fprint(w, tag); err != nil { 617 return err 618 } 619 if wire != WireStartGroup { 620 if err := w.WriteByte(':'); err != nil { 621 return err 622 } 623 } 624 if !w.compact || wire == WireStartGroup { 625 if err := w.WriteByte(' '); err != nil { 626 return err 627 } 628 } 629 switch wire { 630 case WireBytes: 631 buf, e := b.DecodeRawBytes(false) 632 if e == nil { 633 _, err = fmt.Fprintf(w, "%q", buf) 634 } else { 635 _, err = fmt.Fprintf(w, "/* %v */", e) 636 } 637 case WireFixed32: 638 x, err = b.DecodeFixed32() 639 err = writeUnknownInt(w, x, err) 640 case WireFixed64: 641 x, err = b.DecodeFixed64() 642 err = writeUnknownInt(w, x, err) 643 case WireStartGroup: 644 err = w.WriteByte('{') 645 w.indent() 646 case WireVarint: 647 x, err = b.DecodeVarint() 648 err = writeUnknownInt(w, x, err) 649 default: 650 _, err = fmt.Fprintf(w, "/* unknown wire type %d */", wire) 651 } 652 if err != nil { 653 return err 654 } 655 if err = w.WriteByte('\n'); err != nil { 656 return err 657 } 658 } 659 return nil 660} 661 662func writeUnknownInt(w *textWriter, x uint64, err error) error { 663 if err == nil { 664 _, err = fmt.Fprint(w, x) 665 } else { 666 _, err = fmt.Fprintf(w, "/* %v */", err) 667 } 668 return err 669} 670 671type int32Slice []int32 672 673func (s int32Slice) Len() int { return len(s) } 674func (s int32Slice) Less(i, j int) bool { return s[i] < s[j] } 675func (s int32Slice) Swap(i, j int) { s[i], s[j] = s[j], s[i] } 676 677// writeExtensions writes all the extensions in pv. 678// pv is assumed to be a pointer to a protocol message struct that is extendable. 679func (tm *TextMarshaler) writeExtensions(w *textWriter, pv reflect.Value) error { 680 emap := extensionMaps[pv.Type().Elem()] 681 ep, _ := extendable(pv.Interface()) 682 683 // Order the extensions by ID. 684 // This isn't strictly necessary, but it will give us 685 // canonical output, which will also make testing easier. 686 m, mu := ep.extensionsRead() 687 if m == nil { 688 return nil 689 } 690 mu.Lock() 691 ids := make([]int32, 0, len(m)) 692 for id := range m { 693 ids = append(ids, id) 694 } 695 sort.Sort(int32Slice(ids)) 696 mu.Unlock() 697 698 for _, extNum := range ids { 699 ext := m[extNum] 700 var desc *ExtensionDesc 701 if emap != nil { 702 desc = emap[extNum] 703 } 704 if desc == nil { 705 // Unknown extension. 706 if err := writeUnknownStruct(w, ext.enc); err != nil { 707 return err 708 } 709 continue 710 } 711 712 pb, err := GetExtension(ep, desc) 713 if err != nil { 714 return fmt.Errorf("failed getting extension: %v", err) 715 } 716 717 // Repeated extensions will appear as a slice. 718 if !desc.repeated() { 719 if err := tm.writeExtension(w, desc.Name, pb); err != nil { 720 return err 721 } 722 } else { 723 v := reflect.ValueOf(pb) 724 for i := 0; i < v.Len(); i++ { 725 if err := tm.writeExtension(w, desc.Name, v.Index(i).Interface()); err != nil { 726 return err 727 } 728 } 729 } 730 } 731 return nil 732} 733 734func (tm *TextMarshaler) writeExtension(w *textWriter, name string, pb interface{}) error { 735 if _, err := fmt.Fprintf(w, "[%s]:", name); err != nil { 736 return err 737 } 738 if !w.compact { 739 if err := w.WriteByte(' '); err != nil { 740 return err 741 } 742 } 743 if err := tm.writeAny(w, reflect.ValueOf(pb), nil); err != nil { 744 return err 745 } 746 if err := w.WriteByte('\n'); err != nil { 747 return err 748 } 749 return nil 750} 751 752func (w *textWriter) writeIndent() { 753 if !w.complete { 754 return 755 } 756 remain := w.ind * 2 757 for remain > 0 { 758 n := remain 759 if n > len(spaces) { 760 n = len(spaces) 761 } 762 w.w.Write(spaces[:n]) 763 remain -= n 764 } 765 w.complete = false 766} 767 768// TextMarshaler is a configurable text format marshaler. 769type TextMarshaler struct { 770 Compact bool // use compact text format (one line). 771 ExpandAny bool // expand google.protobuf.Any messages of known types 772} 773 774// Marshal writes a given protocol buffer in text format. 775// The only errors returned are from w. 776func (tm *TextMarshaler) Marshal(w io.Writer, pb Message) error { 777 val := reflect.ValueOf(pb) 778 if pb == nil || val.IsNil() { 779 w.Write([]byte("<nil>")) 780 return nil 781 } 782 var bw *bufio.Writer 783 ww, ok := w.(writer) 784 if !ok { 785 bw = bufio.NewWriter(w) 786 ww = bw 787 } 788 aw := &textWriter{ 789 w: ww, 790 complete: true, 791 compact: tm.Compact, 792 } 793 794 if etm, ok := pb.(encoding.TextMarshaler); ok { 795 text, err := etm.MarshalText() 796 if err != nil { 797 return err 798 } 799 if _, err = aw.Write(text); err != nil { 800 return err 801 } 802 if bw != nil { 803 return bw.Flush() 804 } 805 return nil 806 } 807 // Dereference the received pointer so we don't have outer < and >. 808 v := reflect.Indirect(val) 809 if err := tm.writeStruct(aw, v); err != nil { 810 return err 811 } 812 if bw != nil { 813 return bw.Flush() 814 } 815 return nil 816} 817 818// Text is the same as Marshal, but returns the string directly. 819func (tm *TextMarshaler) Text(pb Message) string { 820 var buf bytes.Buffer 821 tm.Marshal(&buf, pb) 822 return buf.String() 823} 824 825var ( 826 defaultTextMarshaler = TextMarshaler{} 827 compactTextMarshaler = TextMarshaler{Compact: true} 828) 829 830// TODO: consider removing some of the Marshal functions below. 831 832// MarshalText writes a given protocol buffer in text format. 833// The only errors returned are from w. 834func MarshalText(w io.Writer, pb Message) error { return defaultTextMarshaler.Marshal(w, pb) } 835 836// MarshalTextString is the same as MarshalText, but returns the string directly. 837func MarshalTextString(pb Message) string { return defaultTextMarshaler.Text(pb) } 838 839// CompactText writes a given protocol buffer in compact text format (one line). 840func CompactText(w io.Writer, pb Message) error { return compactTextMarshaler.Marshal(w, pb) } 841 842// CompactTextString is the same as CompactText, but returns the string directly. 843func CompactTextString(pb Message) string { return compactTextMarshaler.Text(pb) } 844