blob: 507c214d56946186b614f18c70502e74a9b18ecb [file] [log] [blame]
Rob Pikeaaa3a622010-03-20 22:32:34 -07001// Go support for Protocol Buffers - Google's data interchange format
2//
David Symondsee6e9c52012-11-29 08:51:07 +11003// Copyright 2010 The Go Authors. All rights reserved.
Rob Pikeaaa3a622010-03-20 22:32:34 -07004// http://code.google.com/p/goprotobuf/
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
David Symondse37856c2011-06-22 12:52:53 +100034// Functions for writing the text protocol buffer format.
Rob Pikeaaa3a622010-03-20 22:32:34 -070035
36import (
37 "bytes"
38 "fmt"
39 "io"
David Symondse37856c2011-06-22 12:52:53 +100040 "log"
Rob Pikeaaa3a622010-03-20 22:32:34 -070041 "os"
42 "reflect"
David Symonds1d72f7a2011-08-19 18:28:52 +100043 "sort"
Rob Pikeaaa3a622010-03-20 22:32:34 -070044 "strings"
45)
46
David Symondsdbdc4212012-11-08 08:20:35 +110047var (
48 newline = []byte("\n")
49 spaces = []byte(" ")
50 gtNewline = []byte(">\n")
51 endBraceNewline = []byte("}\n")
52 backslashN = []byte{'\\', 'n'}
53 backslashR = []byte{'\\', 'r'}
54 backslashT = []byte{'\\', 't'}
55 backslashDQ = []byte{'\\', '"'}
56 backslashBS = []byte{'\\', '\\'}
57)
58
David Symondse37856c2011-06-22 12:52:53 +100059// textWriter is an io.Writer that tracks its indentation level.
Rob Pikeaaa3a622010-03-20 22:32:34 -070060type textWriter struct {
David Symondsdbdc4212012-11-08 08:20:35 +110061 ind int
62 complete bool // if the current position is a complete line
63 compact bool // whether to write out as a one-liner
64 writer io.Writer
65 writeByte func(byte) error // write a single byte to writer
66}
David Symonds9f402812011-04-28 18:08:44 +100067
David Symondsdbdc4212012-11-08 08:20:35 +110068func (w *textWriter) WriteString(s string) (n int, err error) {
69 if !strings.Contains(s, "\n") {
70 if !w.compact && w.complete {
71 w.writeIndent()
72 }
73 w.complete = false
74 return io.WriteString(w.writer, s)
75 }
76 // WriteString is typically called without newlines, so this
77 // codepath and its copy are rare. We copy to avoid
78 // duplicating all of Write's logic here.
79 return w.Write([]byte(s))
Rob Pikeaaa3a622010-03-20 22:32:34 -070080}
81
Rob Pikea17fdd92011-11-02 12:43:05 -070082func (w *textWriter) Write(p []byte) (n int, err error) {
Rob Pikeaaa3a622010-03-20 22:32:34 -070083 n, err = len(p), nil
84
David Symondsdbdc4212012-11-08 08:20:35 +110085 newlines := bytes.Count(p, newline)
86 if newlines == 0 {
87 if !w.compact && w.complete {
88 w.writeIndent()
89 }
90 w.writer.Write(p)
91 w.complete = false
92 return
93 }
94
95 frags := bytes.SplitN(p, newline, newlines+1)
Rob Pikeaaa3a622010-03-20 22:32:34 -070096 if w.compact {
David Symondsdbdc4212012-11-08 08:20:35 +110097 for i, frag := range frags {
98 if i > 0 {
99 w.writeByte(' ')
100 }
101 w.writer.Write(frag)
102 }
Rob Pikeaaa3a622010-03-20 22:32:34 -0700103 return
104 }
105
David Symondse37856c2011-06-22 12:52:53 +1000106 for i, frag := range frags {
Rob Pikeaaa3a622010-03-20 22:32:34 -0700107 if w.complete {
David Symondsdbdc4212012-11-08 08:20:35 +1100108 w.writeIndent()
Rob Pikeaaa3a622010-03-20 22:32:34 -0700109 }
David Symondsdbdc4212012-11-08 08:20:35 +1100110 w.writer.Write(frag)
Rob Pikeaaa3a622010-03-20 22:32:34 -0700111 if i+1 < len(frags) {
David Symondsdbdc4212012-11-08 08:20:35 +1100112 w.writeByte('\n')
Rob Pikeaaa3a622010-03-20 22:32:34 -0700113 }
114 }
115 w.complete = len(frags[len(frags)-1]) == 0
Rob Pikeaaa3a622010-03-20 22:32:34 -0700116 return
117}
118
Rob Pikea17fdd92011-11-02 12:43:05 -0700119func (w *textWriter) WriteByte(c byte) error {
David Symondsdbdc4212012-11-08 08:20:35 +1100120 if w.compact && c == '\n' {
121 c = ' '
122 }
123 if !w.compact && w.complete {
124 w.writeIndent()
125 }
126 err := w.writeByte(c)
127 w.complete = c == '\n'
David Symonds9f402812011-04-28 18:08:44 +1000128 return err
129}
130
David Symondse37856c2011-06-22 12:52:53 +1000131func (w *textWriter) indent() { w.ind++ }
Rob Pikeaaa3a622010-03-20 22:32:34 -0700132
133func (w *textWriter) unindent() {
David Symondse37856c2011-06-22 12:52:53 +1000134 if w.ind == 0 {
David Symondsd4661c52012-08-30 15:17:53 +1000135 log.Printf("proto: textWriter unindented too far")
David Symondse37856c2011-06-22 12:52:53 +1000136 return
Rob Pikeaaa3a622010-03-20 22:32:34 -0700137 }
David Symondse37856c2011-06-22 12:52:53 +1000138 w.ind--
Rob Pikeaaa3a622010-03-20 22:32:34 -0700139}
140
David Symonds9f402812011-04-28 18:08:44 +1000141func writeName(w *textWriter, props *Properties) {
142 io.WriteString(w, props.OrigName)
143 if props.Wire != "group" {
144 w.WriteByte(':')
145 }
146}
147
David Symonds1d72f7a2011-08-19 18:28:52 +1000148var (
Russ Coxd4ce3f12012-09-12 10:36:26 +1000149 messageSetType = reflect.TypeOf((*MessageSet)(nil)).Elem()
David Symonds1d72f7a2011-08-19 18:28:52 +1000150)
David Symondse37856c2011-06-22 12:52:53 +1000151
David Symondse9e7aaf2012-03-23 13:12:33 +1100152// raw is the interface satisfied by RawMessage.
153type raw interface {
154 Bytes() []byte
155}
156
Rob Pike97e934d2011-04-11 12:52:49 -0700157func writeStruct(w *textWriter, sv reflect.Value) {
David Symonds1d72f7a2011-08-19 18:28:52 +1000158 if sv.Type() == messageSetType {
159 writeMessageSet(w, sv.Addr().Interface().(*MessageSet))
160 return
161 }
162
Rob Pike97e934d2011-04-11 12:52:49 -0700163 st := sv.Type()
Rob Pikeaaa3a622010-03-20 22:32:34 -0700164 sprops := GetProperties(st)
165 for i := 0; i < sv.NumField(); i++ {
David Symonds1d72f7a2011-08-19 18:28:52 +1000166 fv := sv.Field(i)
167 if name := st.Field(i).Name; strings.HasPrefix(name, "XXX_") {
168 // There's only two XXX_ fields:
169 // XXX_unrecognized []byte
170 // XXX_extensions map[int32]proto.Extension
171 // The first is handled here;
172 // the second is handled at the bottom of this function.
173 if name == "XXX_unrecognized" && !fv.IsNil() {
174 writeUnknownStruct(w, fv.Interface().([]byte))
175 }
Rob Pikeaaa3a622010-03-20 22:32:34 -0700176 continue
177 }
178 props := sprops.Prop[i]
Rob Pikeac8b1ce2011-04-11 16:14:54 -0700179 if fv.Kind() == reflect.Ptr && fv.IsNil() {
Rob Pikeaaa3a622010-03-20 22:32:34 -0700180 // Field not filled in. This could be an optional field or
181 // a required field that wasn't filled in. Either way, there
182 // isn't anything we can show for it.
183 continue
184 }
Rob Pikeac8b1ce2011-04-11 16:14:54 -0700185 if fv.Kind() == reflect.Slice && fv.IsNil() {
Rob Pikeaaa3a622010-03-20 22:32:34 -0700186 // Repeated field that is empty, or a bytes field that is unused.
187 continue
188 }
189
David Symondsaa922ff2011-07-19 14:58:06 +1000190 if props.Repeated && fv.Kind() == reflect.Slice {
191 // Repeated field.
192 for j := 0; j < fv.Len(); j++ {
193 writeName(w, props)
194 if !w.compact {
195 w.WriteByte(' ')
Rob Pikeaaa3a622010-03-20 22:32:34 -0700196 }
David Symondsaa922ff2011-07-19 14:58:06 +1000197 writeAny(w, fv.Index(j), props)
198 w.WriteByte('\n')
Rob Pikeaaa3a622010-03-20 22:32:34 -0700199 }
David Symondsaa922ff2011-07-19 14:58:06 +1000200 continue
Rob Pikeaaa3a622010-03-20 22:32:34 -0700201 }
202
David Symonds9f402812011-04-28 18:08:44 +1000203 writeName(w, props)
Rob Pikeaaa3a622010-03-20 22:32:34 -0700204 if !w.compact {
David Symonds9f402812011-04-28 18:08:44 +1000205 w.WriteByte(' ')
Rob Pikeaaa3a622010-03-20 22:32:34 -0700206 }
David Symondse9e7aaf2012-03-23 13:12:33 +1100207 if b, ok := fv.Interface().(raw); ok {
208 writeRaw(w, b.Bytes())
209 continue
210 }
David Symondse37856c2011-06-22 12:52:53 +1000211 if props.Enum != "" && tryWriteEnum(w, props.Enum, fv) {
212 // Enum written.
213 } else {
David Symonds9f402812011-04-28 18:08:44 +1000214 writeAny(w, fv, props)
Rob Pikeaaa3a622010-03-20 22:32:34 -0700215 }
David Symondse37856c2011-06-22 12:52:53 +1000216 w.WriteByte('\n')
217 }
218
David Symonds1d72f7a2011-08-19 18:28:52 +1000219 // Extensions (the XXX_extensions field).
David Symondse37856c2011-06-22 12:52:53 +1000220 pv := sv.Addr()
221 if pv.Type().Implements(extendableProtoType) {
222 writeExtensions(w, pv)
Rob Pikeaaa3a622010-03-20 22:32:34 -0700223 }
224}
225
David Symondse9e7aaf2012-03-23 13:12:33 +1100226// writeRaw writes an uninterpreted raw message.
227func writeRaw(w *textWriter, b []byte) {
228 w.WriteByte('<')
229 if !w.compact {
230 w.WriteByte('\n')
231 }
232 w.indent()
233 writeUnknownStruct(w, b)
234 w.unindent()
235 w.WriteByte('>')
236}
237
David Symondse37856c2011-06-22 12:52:53 +1000238// tryWriteEnum attempts to write an enum value as a symbolic constant.
239// If the enum is unregistered, nothing is written and false is returned.
Rob Pikeaaa3a622010-03-20 22:32:34 -0700240func tryWriteEnum(w *textWriter, enum string, v reflect.Value) bool {
Rob Pikeab5b8022010-06-21 17:47:58 -0700241 v = reflect.Indirect(v)
242 if v.Type().Kind() != reflect.Int32 {
Rob Pikeaaa3a622010-03-20 22:32:34 -0700243 return false
244 }
245 m, ok := enumNameMaps[enum]
246 if !ok {
247 return false
248 }
Rob Pike97e934d2011-04-11 12:52:49 -0700249 str, ok := m[int32(v.Int())]
Rob Pikeaaa3a622010-03-20 22:32:34 -0700250 if !ok {
251 return false
252 }
253 fmt.Fprintf(w, str)
254 return true
255}
256
David Symondse37856c2011-06-22 12:52:53 +1000257// writeAny writes an arbitrary field.
David Symonds9f402812011-04-28 18:08:44 +1000258func writeAny(w *textWriter, v reflect.Value, props *Properties) {
Rob Pikeaaa3a622010-03-20 22:32:34 -0700259 v = reflect.Indirect(v)
260
261 // We don't attempt to serialise every possible value type; only those
262 // that can occur in protocol buffers, plus a few extra that were easy.
David Symondse37856c2011-06-22 12:52:53 +1000263 switch v.Kind() {
Rob Pike97e934d2011-04-11 12:52:49 -0700264 case reflect.Slice:
Rob Pikeaaa3a622010-03-20 22:32:34 -0700265 // Should only be a []byte; repeated fields are handled in writeStruct.
David Symonds4c95bfe2011-09-13 14:43:27 +1000266 writeString(w, string(v.Interface().([]byte)))
Rob Pike97e934d2011-04-11 12:52:49 -0700267 case reflect.String:
David Symonds4c95bfe2011-09-13 14:43:27 +1000268 writeString(w, v.String())
Rob Pike97e934d2011-04-11 12:52:49 -0700269 case reflect.Struct:
Rob Pikeaaa3a622010-03-20 22:32:34 -0700270 // Required/optional group/message.
David Symonds9f402812011-04-28 18:08:44 +1000271 var bra, ket byte = '<', '>'
272 if props != nil && props.Wire == "group" {
273 bra, ket = '{', '}'
274 }
275 w.WriteByte(bra)
Rob Pikeaaa3a622010-03-20 22:32:34 -0700276 if !w.compact {
David Symonds9f402812011-04-28 18:08:44 +1000277 w.WriteByte('\n')
Rob Pikeaaa3a622010-03-20 22:32:34 -0700278 }
279 w.indent()
David Symondse37856c2011-06-22 12:52:53 +1000280 writeStruct(w, v)
Rob Pikeaaa3a622010-03-20 22:32:34 -0700281 w.unindent()
David Symonds9f402812011-04-28 18:08:44 +1000282 w.WriteByte(ket)
Rob Pikeaaa3a622010-03-20 22:32:34 -0700283 default:
David Symondse37856c2011-06-22 12:52:53 +1000284 fmt.Fprint(w, v.Interface())
285 }
286}
287
David Symonds4c95bfe2011-09-13 14:43:27 +1000288// equivalent to C's isprint.
289func isprint(c byte) bool {
290 return c >= 0x20 && c < 0x7f
291}
292
293// writeString writes a string in the protocol buffer text format.
294// It is similar to strconv.Quote except we don't use Go escape sequences,
295// we treat the string as a byte sequence, and we use octal escapes.
296// These differences are to maintain interoperability with the other
297// languages' implementations of the text format.
298func writeString(w *textWriter, s string) {
David Symondsdbdc4212012-11-08 08:20:35 +1100299 w.WriteByte('"') // use WriteByte here to get any needed indent
David Symonds4c95bfe2011-09-13 14:43:27 +1000300 // Loop over the bytes, not the runes.
301 for i := 0; i < len(s); i++ {
302 // Divergence from C++: we don't escape apostrophes.
303 // There's no need to escape them, and the C++ parser
304 // copes with a naked apostrophe.
305 switch c := s[i]; c {
306 case '\n':
David Symondsdbdc4212012-11-08 08:20:35 +1100307 w.writer.Write(backslashN)
David Symonds4c95bfe2011-09-13 14:43:27 +1000308 case '\r':
David Symondsdbdc4212012-11-08 08:20:35 +1100309 w.writer.Write(backslashR)
David Symonds4c95bfe2011-09-13 14:43:27 +1000310 case '\t':
David Symondsdbdc4212012-11-08 08:20:35 +1100311 w.writer.Write(backslashT)
David Symonds4c95bfe2011-09-13 14:43:27 +1000312 case '"':
David Symondsdbdc4212012-11-08 08:20:35 +1100313 w.writer.Write(backslashDQ)
David Symonds4c95bfe2011-09-13 14:43:27 +1000314 case '\\':
David Symondsdbdc4212012-11-08 08:20:35 +1100315 w.writer.Write(backslashBS)
David Symonds4c95bfe2011-09-13 14:43:27 +1000316 default:
317 if isprint(c) {
David Symondsdbdc4212012-11-08 08:20:35 +1100318 w.writeByte(c)
David Symonds4c95bfe2011-09-13 14:43:27 +1000319 } else {
David Symondsdbdc4212012-11-08 08:20:35 +1100320 fmt.Fprintf(w.writer, "\\%03o", c)
David Symonds4c95bfe2011-09-13 14:43:27 +1000321 }
322 }
323 }
David Symonds4c95bfe2011-09-13 14:43:27 +1000324 w.WriteByte('"')
325}
326
David Symonds1d72f7a2011-08-19 18:28:52 +1000327func writeMessageSet(w *textWriter, ms *MessageSet) {
328 for _, item := range ms.Item {
329 id := *item.TypeId
330 if msd, ok := messageSetMap[id]; ok {
331 // Known message set type.
332 fmt.Fprintf(w, "[%s]: <\n", msd.name)
333 w.indent()
334
335 pb := reflect.New(msd.t.Elem())
David Symonds9f60f432012-06-14 09:45:25 +1000336 if err := Unmarshal(item.Message, pb.Interface().(Message)); err != nil {
David Symonds1d72f7a2011-08-19 18:28:52 +1000337 fmt.Fprintf(w, "/* bad message: %v */\n", err)
338 } else {
339 writeStruct(w, pb.Elem())
340 }
341 } else {
342 // Unknown type.
343 fmt.Fprintf(w, "[%d]: <\n", id)
344 w.indent()
345 writeUnknownStruct(w, item.Message)
346 }
347 w.unindent()
David Symondsdbdc4212012-11-08 08:20:35 +1100348 w.Write(gtNewline)
David Symonds1d72f7a2011-08-19 18:28:52 +1000349 }
350}
351
352func writeUnknownStruct(w *textWriter, data []byte) {
353 if !w.compact {
354 fmt.Fprintf(w, "/* %d unknown bytes */\n", len(data))
355 }
356 b := NewBuffer(data)
357 for b.index < len(b.buf) {
358 x, err := b.DecodeVarint()
359 if err != nil {
360 fmt.Fprintf(w, "/* %v */\n", err)
361 return
362 }
363 wire, tag := x&7, x>>3
364 if wire == WireEndGroup {
365 w.unindent()
David Symondsdbdc4212012-11-08 08:20:35 +1100366 w.Write(endBraceNewline)
David Symonds1d72f7a2011-08-19 18:28:52 +1000367 continue
368 }
369 fmt.Fprintf(w, "tag%d", tag)
370 if wire != WireStartGroup {
371 w.WriteByte(':')
372 }
373 if !w.compact || wire == WireStartGroup {
374 w.WriteByte(' ')
375 }
376 switch wire {
377 case WireBytes:
378 buf, err := b.DecodeRawBytes(false)
379 if err == nil {
380 fmt.Fprintf(w, "%q", buf)
381 } else {
382 fmt.Fprintf(w, "/* %v */", err)
383 }
384 case WireFixed32:
385 x, err := b.DecodeFixed32()
386 writeUnknownInt(w, x, err)
387 case WireFixed64:
388 x, err := b.DecodeFixed64()
389 writeUnknownInt(w, x, err)
390 case WireStartGroup:
David Symondsdbdc4212012-11-08 08:20:35 +1100391 w.WriteByte('{')
David Symonds1d72f7a2011-08-19 18:28:52 +1000392 w.indent()
393 case WireVarint:
394 x, err := b.DecodeVarint()
395 writeUnknownInt(w, x, err)
396 default:
397 fmt.Fprintf(w, "/* unknown wire type %d */", wire)
398 }
399 w.WriteByte('\n')
400 }
401}
402
Rob Pikea17fdd92011-11-02 12:43:05 -0700403func writeUnknownInt(w *textWriter, x uint64, err error) {
David Symonds1d72f7a2011-08-19 18:28:52 +1000404 if err == nil {
405 fmt.Fprint(w, x)
406 } else {
407 fmt.Fprintf(w, "/* %v */", err)
408 }
409}
410
411type int32Slice []int32
412
413func (s int32Slice) Len() int { return len(s) }
414func (s int32Slice) Less(i, j int) bool { return s[i] < s[j] }
415func (s int32Slice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
416
David Symondse37856c2011-06-22 12:52:53 +1000417// writeExtensions writes all the extensions in pv.
418// pv is assumed to be a pointer to a protocol message struct that is extendable.
419func writeExtensions(w *textWriter, pv reflect.Value) {
420 emap := extensionMaps[pv.Type().Elem()]
421 ep := pv.Interface().(extendableProto)
David Symonds1d72f7a2011-08-19 18:28:52 +1000422
423 // Order the extensions by ID.
424 // This isn't strictly necessary, but it will give us
425 // canonical output, which will also make testing easier.
426 m := ep.ExtensionMap()
427 ids := make([]int32, 0, len(m))
428 for id := range m {
429 ids = append(ids, id)
430 }
431 sort.Sort(int32Slice(ids))
432
433 for _, extNum := range ids {
434 ext := m[extNum]
David Symondse37856c2011-06-22 12:52:53 +1000435 var desc *ExtensionDesc
436 if emap != nil {
437 desc = emap[extNum]
438 }
439 if desc == nil {
David Symonds1d72f7a2011-08-19 18:28:52 +1000440 // Unknown extension.
441 writeUnknownStruct(w, ext.enc)
David Symondse37856c2011-06-22 12:52:53 +1000442 continue
443 }
444
445 pb, err := GetExtension(ep, desc)
446 if err != nil {
447 fmt.Fprintln(os.Stderr, "proto: failed getting extension: ", err)
448 continue
449 }
450
David Symonds61826da2012-05-05 09:31:28 +1000451 // Repeated extensions will appear as a slice.
452 if !desc.repeated() {
453 writeExtension(w, desc.Name, pb)
454 } else {
455 v := reflect.ValueOf(pb)
456 for i := 0; i < v.Len(); i++ {
457 writeExtension(w, desc.Name, v.Index(i).Interface())
458 }
David Symondse37856c2011-06-22 12:52:53 +1000459 }
Rob Pikeaaa3a622010-03-20 22:32:34 -0700460 }
461}
462
David Symonds61826da2012-05-05 09:31:28 +1000463func writeExtension(w *textWriter, name string, pb interface{}) {
464 fmt.Fprintf(w, "[%s]:", name)
465 if !w.compact {
466 w.WriteByte(' ')
467 }
468 writeAny(w, reflect.ValueOf(pb), nil)
469 w.WriteByte('\n')
470}
471
David Symondsdbdc4212012-11-08 08:20:35 +1100472func (w *textWriter) writeIndent() {
473 if !w.complete {
474 return
475 }
476 remain := w.ind * 2
477 for remain > 0 {
478 n := remain
479 if n > len(spaces) {
480 n = len(spaces)
481 }
482 w.writer.Write(spaces[:n])
483 remain -= n
484 }
485 w.complete = false
486}
487
488type byteWriter interface {
489 WriteByte(byte) error
490}
491
David Symonds9f60f432012-06-14 09:45:25 +1000492func marshalText(w io.Writer, pb Message, compact bool) {
David Symonds03c9d412010-08-26 14:23:18 +1000493 if pb == nil {
494 w.Write([]byte("<nil>"))
495 return
496 }
Rob Pikeaaa3a622010-03-20 22:32:34 -0700497 aw := new(textWriter)
498 aw.writer = w
499 aw.complete = true
500 aw.compact = compact
501
David Symondsdbdc4212012-11-08 08:20:35 +1100502 if bw, ok := w.(byteWriter); ok {
503 aw.writeByte = func(c byte) error {
504 return bw.WriteByte(c)
505 }
506 } else {
507 var scratch [1]byte
508 aw.writeByte = func(c byte) error {
509 scratch[0] = c
510 _, err := w.Write(scratch[:])
511 return err
512 }
513 }
514
David Symonds92dd6c12012-03-23 10:59:49 +1100515 // Dereference the received pointer so we don't have outer < and >.
David Symonds9f60f432012-06-14 09:45:25 +1000516 v := reflect.Indirect(reflect.ValueOf(pb))
517 writeStruct(aw, v)
Rob Pikeaaa3a622010-03-20 22:32:34 -0700518}
519
David Symondse37856c2011-06-22 12:52:53 +1000520// MarshalText writes a given protocol buffer in text format.
David Symonds9f60f432012-06-14 09:45:25 +1000521func MarshalText(w io.Writer, pb Message) { marshalText(w, pb, false) }
Rob Pikeaaa3a622010-03-20 22:32:34 -0700522
David Symondsd2bff3c2012-03-14 10:45:25 +1100523// MarshalTextString is the same as MarshalText, but returns the string directly.
David Symonds9f60f432012-06-14 09:45:25 +1000524func MarshalTextString(pb Message) string {
David Symondsd2bff3c2012-03-14 10:45:25 +1100525 var buf bytes.Buffer
526 marshalText(&buf, pb, false)
527 return buf.String()
528}
529
David Symondsd4661c52012-08-30 15:17:53 +1000530// CompactText writes a given protocol buffer in compact text format (one line).
David Symonds9f60f432012-06-14 09:45:25 +1000531func CompactText(w io.Writer, pb Message) { marshalText(w, pb, true) }
Rob Pikeaaa3a622010-03-20 22:32:34 -0700532
533// CompactTextString is the same as CompactText, but returns the string directly.
David Symonds9f60f432012-06-14 09:45:25 +1000534func CompactTextString(pb Message) string {
David Symonds183124e2012-03-23 13:20:23 +1100535 var buf bytes.Buffer
536 marshalText(&buf, pb, true)
Rob Pikeaaa3a622010-03-20 22:32:34 -0700537 return buf.String()
538}