1
2
3
4
5 package text
6
7 import (
8 "math"
9 "math/bits"
10 "strconv"
11 "strings"
12 "unicode/utf8"
13
14 "google.golang.org/protobuf/internal/detrand"
15 "google.golang.org/protobuf/internal/errors"
16 )
17
18
19 type encType uint8
20
21 const (
22 _ encType = (1 << iota) / 2
23 name
24 scalar
25 messageOpen
26 messageClose
27 )
28
29
30
31 type Encoder struct {
32 encoderState
33
34 indent string
35 delims [2]byte
36 outputASCII bool
37 }
38
39 type encoderState struct {
40 lastType encType
41 indents []byte
42 out []byte
43 }
44
45
46
47
48
49
50
51
52
53
54
55
56 func NewEncoder(buf []byte, indent string, delims [2]byte, outputASCII bool) (*Encoder, error) {
57 e := &Encoder{
58 encoderState: encoderState{out: buf},
59 }
60 if len(indent) > 0 {
61 if strings.Trim(indent, " \t") != "" {
62 return nil, errors.New("indent may only be composed of space and tab characters")
63 }
64 e.indent = indent
65 }
66 switch delims {
67 case [2]byte{0, 0}:
68 e.delims = [2]byte{'{', '}'}
69 case [2]byte{'{', '}'}, [2]byte{'<', '>'}:
70 e.delims = delims
71 default:
72 return nil, errors.New("delimiters may only be \"{}\" or \"<>\"")
73 }
74 e.outputASCII = outputASCII
75
76 return e, nil
77 }
78
79
80 func (e *Encoder) Bytes() []byte {
81 return e.out
82 }
83
84
85 func (e *Encoder) StartMessage() {
86 e.prepareNext(messageOpen)
87 e.out = append(e.out, e.delims[0])
88 }
89
90
91 func (e *Encoder) EndMessage() {
92 e.prepareNext(messageClose)
93 e.out = append(e.out, e.delims[1])
94 }
95
96
97 func (e *Encoder) WriteName(s string) {
98 e.prepareNext(name)
99 e.out = append(e.out, s...)
100 e.out = append(e.out, ':')
101 }
102
103
104 func (e *Encoder) WriteBool(b bool) {
105 if b {
106 e.WriteLiteral("true")
107 } else {
108 e.WriteLiteral("false")
109 }
110 }
111
112
113 func (e *Encoder) WriteString(s string) {
114 e.prepareNext(scalar)
115 e.out = appendString(e.out, s, e.outputASCII)
116 }
117
118 func appendString(out []byte, in string, outputASCII bool) []byte {
119 out = append(out, '"')
120 i := indexNeedEscapeInString(in)
121 in, out = in[i:], append(out, in[:i]...)
122 for len(in) > 0 {
123 switch r, n := utf8.DecodeRuneInString(in); {
124 case r == utf8.RuneError && n == 1:
125
126
127 r = rune(in[0])
128 fallthrough
129 case r < ' ' || r == '"' || r == '\\' || r == 0x7f:
130 out = append(out, '\\')
131 switch r {
132 case '"', '\\':
133 out = append(out, byte(r))
134 case '\n':
135 out = append(out, 'n')
136 case '\r':
137 out = append(out, 'r')
138 case '\t':
139 out = append(out, 't')
140 default:
141 out = append(out, 'x')
142 out = append(out, "00"[1+(bits.Len32(uint32(r))-1)/4:]...)
143 out = strconv.AppendUint(out, uint64(r), 16)
144 }
145 in = in[n:]
146 case r >= utf8.RuneSelf && (outputASCII || r <= 0x009f):
147 out = append(out, '\\')
148 if r <= math.MaxUint16 {
149 out = append(out, 'u')
150 out = append(out, "0000"[1+(bits.Len32(uint32(r))-1)/4:]...)
151 out = strconv.AppendUint(out, uint64(r), 16)
152 } else {
153 out = append(out, 'U')
154 out = append(out, "00000000"[1+(bits.Len32(uint32(r))-1)/4:]...)
155 out = strconv.AppendUint(out, uint64(r), 16)
156 }
157 in = in[n:]
158 default:
159 i := indexNeedEscapeInString(in[n:])
160 in, out = in[n+i:], append(out, in[:n+i]...)
161 }
162 }
163 out = append(out, '"')
164 return out
165 }
166
167
168
169 func indexNeedEscapeInString(s string) int {
170 for i := 0; i < len(s); i++ {
171 if c := s[i]; c < ' ' || c == '"' || c == '\'' || c == '\\' || c >= 0x7f {
172 return i
173 }
174 }
175 return len(s)
176 }
177
178
179 func (e *Encoder) WriteFloat(n float64, bitSize int) {
180 e.prepareNext(scalar)
181 e.out = appendFloat(e.out, n, bitSize)
182 }
183
184 func appendFloat(out []byte, n float64, bitSize int) []byte {
185 switch {
186 case math.IsNaN(n):
187 return append(out, "nan"...)
188 case math.IsInf(n, +1):
189 return append(out, "inf"...)
190 case math.IsInf(n, -1):
191 return append(out, "-inf"...)
192 default:
193 return strconv.AppendFloat(out, n, 'g', -1, bitSize)
194 }
195 }
196
197
198 func (e *Encoder) WriteInt(n int64) {
199 e.prepareNext(scalar)
200 e.out = strconv.AppendInt(e.out, n, 10)
201 }
202
203
204 func (e *Encoder) WriteUint(n uint64) {
205 e.prepareNext(scalar)
206 e.out = strconv.AppendUint(e.out, n, 10)
207 }
208
209
210
211 func (e *Encoder) WriteLiteral(s string) {
212 e.prepareNext(scalar)
213 e.out = append(e.out, s...)
214 }
215
216
217
218 func (e *Encoder) prepareNext(next encType) {
219 defer func() {
220 e.lastType = next
221 }()
222
223
224 if len(e.indent) == 0 {
225
226 if e.lastType&(scalar|messageClose) != 0 && next == name {
227 e.out = append(e.out, ' ')
228
229 if detrand.Bool() {
230 e.out = append(e.out, ' ')
231 }
232 }
233 return
234 }
235
236
237 switch {
238 case e.lastType == name:
239 e.out = append(e.out, ' ')
240
241 if detrand.Bool() {
242 e.out = append(e.out, ' ')
243 }
244
245 case e.lastType == messageOpen && next != messageClose:
246 e.indents = append(e.indents, e.indent...)
247 e.out = append(e.out, '\n')
248 e.out = append(e.out, e.indents...)
249
250 case e.lastType&(scalar|messageClose) != 0:
251 if next == messageClose {
252 e.indents = e.indents[:len(e.indents)-len(e.indent)]
253 }
254 e.out = append(e.out, '\n')
255 e.out = append(e.out, e.indents...)
256 }
257 }
258
259
260 func (e *Encoder) Snapshot() encoderState {
261 return e.encoderState
262 }
263
264
265 func (e *Encoder) Reset(es encoderState) {
266 e.encoderState = es
267 }
268
269
270 func AppendString(b []byte, s string) []byte {
271 return appendString(b, s, false)
272 }
273
View as plain text