podman

Форк
0
278 строк · 7.1 Кб
1
// 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

5
package json
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
// kind represents an encoding type.
19
type kind uint8
20

21
const (
22
	_ kind = (1 << iota) / 2
23
	name
24
	scalar
25
	objectOpen
26
	objectClose
27
	arrayOpen
28
	arrayClose
29
)
30

31
// Encoder provides methods to write out JSON constructs and values. The user is
32
// responsible for producing valid sequences of JSON constructs and values.
33
type Encoder struct {
34
	indent   string
35
	lastKind kind
36
	indents  []byte
37
	out      []byte
38
}
39

40
// NewEncoder returns an Encoder.
41
//
42
// If indent is a non-empty string, it causes every entry for an Array or Object
43
// to be preceded by the indent and trailed by a newline.
44
func NewEncoder(buf []byte, indent string) (*Encoder, error) {
45
	e := &Encoder{
46
		out: buf,
47
	}
48
	if len(indent) > 0 {
49
		if strings.Trim(indent, " \t") != "" {
50
			return nil, errors.New("indent may only be composed of space or tab characters")
51
		}
52
		e.indent = indent
53
	}
54
	return e, nil
55
}
56

57
// Bytes returns the content of the written bytes.
58
func (e *Encoder) Bytes() []byte {
59
	return e.out
60
}
61

62
// WriteNull writes out the null value.
63
func (e *Encoder) WriteNull() {
64
	e.prepareNext(scalar)
65
	e.out = append(e.out, "null"...)
66
}
67

68
// WriteBool writes out the given boolean value.
69
func (e *Encoder) WriteBool(b bool) {
70
	e.prepareNext(scalar)
71
	if b {
72
		e.out = append(e.out, "true"...)
73
	} else {
74
		e.out = append(e.out, "false"...)
75
	}
76
}
77

78
// WriteString writes out the given string in JSON string value. Returns error
79
// if input string contains invalid UTF-8.
80
func (e *Encoder) WriteString(s string) error {
81
	e.prepareNext(scalar)
82
	var err error
83
	if e.out, err = appendString(e.out, s); err != nil {
84
		return err
85
	}
86
	return nil
87
}
88

89
// Sentinel error used for indicating invalid UTF-8.
90
var errInvalidUTF8 = errors.New("invalid UTF-8")
91

92
func appendString(out []byte, in string) ([]byte, error) {
93
	out = append(out, '"')
94
	i := indexNeedEscapeInString(in)
95
	in, out = in[i:], append(out, in[:i]...)
96
	for len(in) > 0 {
97
		switch r, n := utf8.DecodeRuneInString(in); {
98
		case r == utf8.RuneError && n == 1:
99
			return out, errInvalidUTF8
100
		case r < ' ' || r == '"' || r == '\\':
101
			out = append(out, '\\')
102
			switch r {
103
			case '"', '\\':
104
				out = append(out, byte(r))
105
			case '\b':
106
				out = append(out, 'b')
107
			case '\f':
108
				out = append(out, 'f')
109
			case '\n':
110
				out = append(out, 'n')
111
			case '\r':
112
				out = append(out, 'r')
113
			case '\t':
114
				out = append(out, 't')
115
			default:
116
				out = append(out, 'u')
117
				out = append(out, "0000"[1+(bits.Len32(uint32(r))-1)/4:]...)
118
				out = strconv.AppendUint(out, uint64(r), 16)
119
			}
120
			in = in[n:]
121
		default:
122
			i := indexNeedEscapeInString(in[n:])
123
			in, out = in[n+i:], append(out, in[:n+i]...)
124
		}
125
	}
126
	out = append(out, '"')
127
	return out, nil
128
}
129

130
// indexNeedEscapeInString returns the index of the character that needs
131
// escaping. If no characters need escaping, this returns the input length.
132
func indexNeedEscapeInString(s string) int {
133
	for i, r := range s {
134
		if r < ' ' || r == '\\' || r == '"' || r == utf8.RuneError {
135
			return i
136
		}
137
	}
138
	return len(s)
139
}
140

141
// WriteFloat writes out the given float and bitSize in JSON number value.
142
func (e *Encoder) WriteFloat(n float64, bitSize int) {
143
	e.prepareNext(scalar)
144
	e.out = appendFloat(e.out, n, bitSize)
145
}
146

147
// appendFloat formats given float in bitSize, and appends to the given []byte.
148
func appendFloat(out []byte, n float64, bitSize int) []byte {
149
	switch {
150
	case math.IsNaN(n):
151
		return append(out, `"NaN"`...)
152
	case math.IsInf(n, +1):
153
		return append(out, `"Infinity"`...)
154
	case math.IsInf(n, -1):
155
		return append(out, `"-Infinity"`...)
156
	}
157

158
	// JSON number formatting logic based on encoding/json.
159
	// See floatEncoder.encode for reference.
160
	fmt := byte('f')
161
	if abs := math.Abs(n); abs != 0 {
162
		if bitSize == 64 && (abs < 1e-6 || abs >= 1e21) ||
163
			bitSize == 32 && (float32(abs) < 1e-6 || float32(abs) >= 1e21) {
164
			fmt = 'e'
165
		}
166
	}
167
	out = strconv.AppendFloat(out, n, fmt, -1, bitSize)
168
	if fmt == 'e' {
169
		n := len(out)
170
		if n >= 4 && out[n-4] == 'e' && out[n-3] == '-' && out[n-2] == '0' {
171
			out[n-2] = out[n-1]
172
			out = out[:n-1]
173
		}
174
	}
175
	return out
176
}
177

178
// WriteInt writes out the given signed integer in JSON number value.
179
func (e *Encoder) WriteInt(n int64) {
180
	e.prepareNext(scalar)
181
	e.out = strconv.AppendInt(e.out, n, 10)
182
}
183

184
// WriteUint writes out the given unsigned integer in JSON number value.
185
func (e *Encoder) WriteUint(n uint64) {
186
	e.prepareNext(scalar)
187
	e.out = strconv.AppendUint(e.out, n, 10)
188
}
189

190
// StartObject writes out the '{' symbol.
191
func (e *Encoder) StartObject() {
192
	e.prepareNext(objectOpen)
193
	e.out = append(e.out, '{')
194
}
195

196
// EndObject writes out the '}' symbol.
197
func (e *Encoder) EndObject() {
198
	e.prepareNext(objectClose)
199
	e.out = append(e.out, '}')
200
}
201

202
// WriteName writes out the given string in JSON string value and the name
203
// separator ':'. Returns error if input string contains invalid UTF-8, which
204
// should not be likely as protobuf field names should be valid.
205
func (e *Encoder) WriteName(s string) error {
206
	e.prepareNext(name)
207
	var err error
208
	// Append to output regardless of error.
209
	e.out, err = appendString(e.out, s)
210
	e.out = append(e.out, ':')
211
	return err
212
}
213

214
// StartArray writes out the '[' symbol.
215
func (e *Encoder) StartArray() {
216
	e.prepareNext(arrayOpen)
217
	e.out = append(e.out, '[')
218
}
219

220
// EndArray writes out the ']' symbol.
221
func (e *Encoder) EndArray() {
222
	e.prepareNext(arrayClose)
223
	e.out = append(e.out, ']')
224
}
225

226
// prepareNext adds possible comma and indentation for the next value based
227
// on last type and indent option. It also updates lastKind to next.
228
func (e *Encoder) prepareNext(next kind) {
229
	defer func() {
230
		// Set lastKind to next.
231
		e.lastKind = next
232
	}()
233

234
	if len(e.indent) == 0 {
235
		// Need to add comma on the following condition.
236
		if e.lastKind&(scalar|objectClose|arrayClose) != 0 &&
237
			next&(name|scalar|objectOpen|arrayOpen) != 0 {
238
			e.out = append(e.out, ',')
239
			// For single-line output, add a random extra space after each
240
			// comma to make output unstable.
241
			if detrand.Bool() {
242
				e.out = append(e.out, ' ')
243
			}
244
		}
245
		return
246
	}
247

248
	switch {
249
	case e.lastKind&(objectOpen|arrayOpen) != 0:
250
		// If next type is NOT closing, add indent and newline.
251
		if next&(objectClose|arrayClose) == 0 {
252
			e.indents = append(e.indents, e.indent...)
253
			e.out = append(e.out, '\n')
254
			e.out = append(e.out, e.indents...)
255
		}
256

257
	case e.lastKind&(scalar|objectClose|arrayClose) != 0:
258
		switch {
259
		// If next type is either a value or name, add comma and newline.
260
		case next&(name|scalar|objectOpen|arrayOpen) != 0:
261
			e.out = append(e.out, ',', '\n')
262

263
		// If next type is a closing object or array, adjust indentation.
264
		case next&(objectClose|arrayClose) != 0:
265
			e.indents = e.indents[:len(e.indents)-len(e.indent)]
266
			e.out = append(e.out, '\n')
267
		}
268
		e.out = append(e.out, e.indents...)
269

270
	case e.lastKind&name != 0:
271
		e.out = append(e.out, ' ')
272
		// For multi-line output, add a random extra space after key: to make
273
		// output unstable.
274
		if detrand.Bool() {
275
			e.out = append(e.out, ' ')
276
		}
277
	}
278
}
279

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.