blob: 982037bf87eb835a352ff02619cc1a4ab055af06 [file] [log] [blame]
Joe Tsai27c2a762018-08-01 16:48:18 -07001// Copyright 2018 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package text
6
7import (
8 "bytes"
9 "strings"
10
Damien Neile89e6242019-05-13 23:55:40 -070011 "google.golang.org/protobuf/internal/detrand"
12 "google.golang.org/protobuf/internal/errors"
Joe Tsai27c2a762018-08-01 16:48:18 -070013)
14
15// Marshal serializes v as the proto text format, where v must be a Message.
16// In the proto text format, the top-level value is always a message where the
17// delimiters are elided.
18//
19// If indent is a non-empty string, it causes every entry in a List or Message
20// to be preceded by the indent and trailed by a newline.
21//
22// If delims is not the zero value, it controls the delimiter characters used
23// for messages (e.g., "{}" vs "<>").
24//
25// If outputASCII is true, strings will be serialized in such a way that
26// multi-byte UTF-8 sequences are escaped. This property ensures that the
27// overall output is ASCII (as opposed to UTF-8).
28func Marshal(v Value, indent string, delims [2]byte, outputASCII bool) ([]byte, error) {
29 p := encoder{}
30 if len(indent) > 0 {
31 if strings.Trim(indent, " \t") != "" {
32 return nil, errors.New("indent may only be composed of space and tab characters")
33 }
34 p.indent = indent
35 p.newline = "\n"
36 }
37 switch delims {
38 case [2]byte{0, 0}:
39 p.delims = [2]byte{'{', '}'}
40 case [2]byte{'{', '}'}, [2]byte{'<', '>'}:
41 p.delims = delims
42 default:
43 return nil, errors.New("delimiters may only be \"{}\" or \"<>\"")
44 }
45 p.outputASCII = outputASCII
46
47 err := p.marshalMessage(v, false)
Damien Neil8c86fc52019-06-19 09:28:29 -070048 if err != nil {
Joe Tsai27c2a762018-08-01 16:48:18 -070049 return nil, err
50 }
51 if len(indent) > 0 {
Damien Neil8c86fc52019-06-19 09:28:29 -070052 return append(bytes.TrimRight(p.out, "\n"), '\n'), nil
Joe Tsai27c2a762018-08-01 16:48:18 -070053 }
Damien Neil8c86fc52019-06-19 09:28:29 -070054 return p.out, nil
Joe Tsai27c2a762018-08-01 16:48:18 -070055}
56
57type encoder struct {
Damien Neil8c86fc52019-06-19 09:28:29 -070058 out []byte
Joe Tsai27c2a762018-08-01 16:48:18 -070059
60 indent string
61 indents []byte
62 newline string // set to "\n" if len(indent) > 0
63 delims [2]byte
64 outputASCII bool
65}
66
67func (p *encoder) marshalList(v Value) error {
68 if v.Type() != List {
69 return errors.New("invalid type %v, expected list", v.Type())
70 }
71 elems := v.List()
72 p.out = append(p.out, '[')
73 p.indents = append(p.indents, p.indent...)
74 if len(elems) > 0 {
75 p.out = append(p.out, p.newline...)
76 }
77 for i, elem := range elems {
78 p.out = append(p.out, p.indents...)
Damien Neil8c86fc52019-06-19 09:28:29 -070079 if err := p.marshalValue(elem); err != nil {
Joe Tsai27c2a762018-08-01 16:48:18 -070080 return err
81 }
82 if i < len(elems)-1 {
83 p.out = append(p.out, ',')
84 }
85 p.out = append(p.out, p.newline...)
86 }
87 p.indents = p.indents[:len(p.indents)-len(p.indent)]
88 if len(elems) > 0 {
89 p.out = append(p.out, p.indents...)
90 }
91 p.out = append(p.out, ']')
92 return nil
93}
94
95func (p *encoder) marshalMessage(v Value, emitDelims bool) error {
96 if v.Type() != Message {
97 return errors.New("invalid type %v, expected message", v.Type())
98 }
99 items := v.Message()
100 if emitDelims {
101 p.out = append(p.out, p.delims[0])
102 p.indents = append(p.indents, p.indent...)
103 if len(items) > 0 {
104 p.out = append(p.out, p.newline...)
105 }
106 }
107 for i, item := range items {
108 p.out = append(p.out, p.indents...)
Damien Neil8c86fc52019-06-19 09:28:29 -0700109 if err := p.marshalKey(item[0]); err != nil {
Joe Tsai27c2a762018-08-01 16:48:18 -0700110 return err
111 }
112 p.out = append(p.out, ':')
113 if len(p.indent) > 0 {
114 p.out = append(p.out, ' ')
115 }
Herbie Ongc3f4d482018-11-22 14:29:07 -0800116 // For multi-line output, add a random extra space after key: per message to
117 // make output unstable.
Joe Tsai492a4762018-11-26 17:16:32 -0800118 if len(p.indent) > 0 && detrand.Bool() {
Herbie Ongc3f4d482018-11-22 14:29:07 -0800119 p.out = append(p.out, ' ')
Herbie Ongc3f4d482018-11-22 14:29:07 -0800120 }
121
Damien Neil8c86fc52019-06-19 09:28:29 -0700122 if err := p.marshalValue(item[1]); err != nil {
Joe Tsai27c2a762018-08-01 16:48:18 -0700123 return err
124 }
125 if i < len(items)-1 && len(p.indent) == 0 {
126 p.out = append(p.out, ' ')
127 }
Herbie Ongc3f4d482018-11-22 14:29:07 -0800128 // For single-line output, add a random extra space after a field per message to
129 // make output unstable.
Joe Tsai492a4762018-11-26 17:16:32 -0800130 if len(p.indent) == 0 && detrand.Bool() && i != len(items)-1 {
Herbie Ongc3f4d482018-11-22 14:29:07 -0800131 p.out = append(p.out, ' ')
Herbie Ongc3f4d482018-11-22 14:29:07 -0800132 }
Joe Tsai27c2a762018-08-01 16:48:18 -0700133 p.out = append(p.out, p.newline...)
134 }
135 if emitDelims {
136 p.indents = p.indents[:len(p.indents)-len(p.indent)]
137 if len(items) > 0 {
138 p.out = append(p.out, p.indents...)
139 }
140 p.out = append(p.out, p.delims[1])
141 }
142 return nil
143}
144
145func (p *encoder) marshalKey(v Value) error {
146 switch v.Type() {
147 case String:
148 var err error
149 p.out = append(p.out, '[')
150 if len(urlRegexp.FindString(v.str)) == len(v.str) {
151 p.out = append(p.out, v.str...)
152 } else {
153 err = p.marshalString(v)
154 }
155 p.out = append(p.out, ']')
156 return err
157 case Uint:
158 return p.marshalNumber(v)
159 case Name:
160 s, _ := v.Name()
161 p.out = append(p.out, s...)
162 return nil
163 default:
164 return errors.New("invalid type %v to encode key", v.Type())
165 }
166}
167
168func (p *encoder) marshalValue(v Value) error {
169 switch v.Type() {
Herbie Ong84f09602019-01-17 19:31:47 -0800170 case Bool, Int, Uint, Float32, Float64:
Joe Tsai27c2a762018-08-01 16:48:18 -0700171 return p.marshalNumber(v)
172 case String:
173 return p.marshalString(v)
174 case List:
175 return p.marshalList(v)
176 case Message:
177 return p.marshalMessage(v, true)
178 case Name:
179 s, _ := v.Name()
180 p.out = append(p.out, s...)
181 return nil
182 default:
183 return errors.New("invalid type %v to encode value", v.Type())
184 }
185}