1// Package jwriter contains a JSON writer. 2package jwriter 3 4import ( 5 "io" 6 "strconv" 7 "unicode/utf8" 8 9 "github.com/mailru/easyjson/buffer" 10) 11 12// Flags describe various encoding options. The behavior may be actually implemented in the encoder, but 13// Flags field in Writer is used to set and pass them around. 14type Flags int 15 16const ( 17 NilMapAsEmpty Flags = 1 << iota // Encode nil map as '{}' rather than 'null'. 18 NilSliceAsEmpty // Encode nil slice as '[]' rather than 'null'. 19) 20 21// Writer is a JSON writer. 22type Writer struct { 23 Flags Flags 24 25 Error error 26 Buffer buffer.Buffer 27 NoEscapeHTML bool 28} 29 30// Size returns the size of the data that was written out. 31func (w *Writer) Size() int { 32 return w.Buffer.Size() 33} 34 35// DumpTo outputs the data to given io.Writer, resetting the buffer. 36func (w *Writer) DumpTo(out io.Writer) (written int, err error) { 37 return w.Buffer.DumpTo(out) 38} 39 40// BuildBytes returns writer data as a single byte slice. You can optionally provide one byte slice 41// as argument that it will try to reuse. 42func (w *Writer) BuildBytes(reuse ...[]byte) ([]byte, error) { 43 if w.Error != nil { 44 return nil, w.Error 45 } 46 47 return w.Buffer.BuildBytes(reuse...), nil 48} 49 50// ReadCloser returns an io.ReadCloser that can be used to read the data. 51// ReadCloser also resets the buffer. 52func (w *Writer) ReadCloser() (io.ReadCloser, error) { 53 if w.Error != nil { 54 return nil, w.Error 55 } 56 57 return w.Buffer.ReadCloser(), nil 58} 59 60// RawByte appends raw binary data to the buffer. 61func (w *Writer) RawByte(c byte) { 62 w.Buffer.AppendByte(c) 63} 64 65// RawByte appends raw binary data to the buffer. 66func (w *Writer) RawString(s string) { 67 w.Buffer.AppendString(s) 68} 69 70// Raw appends raw binary data to the buffer or sets the error if it is given. Useful for 71// calling with results of MarshalJSON-like functions. 72func (w *Writer) Raw(data []byte, err error) { 73 switch { 74 case w.Error != nil: 75 return 76 case err != nil: 77 w.Error = err 78 case len(data) > 0: 79 w.Buffer.AppendBytes(data) 80 default: 81 w.RawString("null") 82 } 83} 84 85// RawText encloses raw binary data in quotes and appends in to the buffer. 86// Useful for calling with results of MarshalText-like functions. 87func (w *Writer) RawText(data []byte, err error) { 88 switch { 89 case w.Error != nil: 90 return 91 case err != nil: 92 w.Error = err 93 case len(data) > 0: 94 w.String(string(data)) 95 default: 96 w.RawString("null") 97 } 98} 99 100// Base64Bytes appends data to the buffer after base64 encoding it 101func (w *Writer) Base64Bytes(data []byte) { 102 if data == nil { 103 w.Buffer.AppendString("null") 104 return 105 } 106 w.Buffer.AppendByte('"') 107 w.base64(data) 108 w.Buffer.AppendByte('"') 109} 110 111func (w *Writer) Uint8(n uint8) { 112 w.Buffer.EnsureSpace(3) 113 w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10) 114} 115 116func (w *Writer) Uint16(n uint16) { 117 w.Buffer.EnsureSpace(5) 118 w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10) 119} 120 121func (w *Writer) Uint32(n uint32) { 122 w.Buffer.EnsureSpace(10) 123 w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10) 124} 125 126func (w *Writer) Uint(n uint) { 127 w.Buffer.EnsureSpace(20) 128 w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10) 129} 130 131func (w *Writer) Uint64(n uint64) { 132 w.Buffer.EnsureSpace(20) 133 w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, n, 10) 134} 135 136func (w *Writer) Int8(n int8) { 137 w.Buffer.EnsureSpace(4) 138 w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10) 139} 140 141func (w *Writer) Int16(n int16) { 142 w.Buffer.EnsureSpace(6) 143 w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10) 144} 145 146func (w *Writer) Int32(n int32) { 147 w.Buffer.EnsureSpace(11) 148 w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10) 149} 150 151func (w *Writer) Int(n int) { 152 w.Buffer.EnsureSpace(21) 153 w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10) 154} 155 156func (w *Writer) Int64(n int64) { 157 w.Buffer.EnsureSpace(21) 158 w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, n, 10) 159} 160 161func (w *Writer) Uint8Str(n uint8) { 162 w.Buffer.EnsureSpace(3) 163 w.Buffer.Buf = append(w.Buffer.Buf, '"') 164 w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10) 165 w.Buffer.Buf = append(w.Buffer.Buf, '"') 166} 167 168func (w *Writer) Uint16Str(n uint16) { 169 w.Buffer.EnsureSpace(5) 170 w.Buffer.Buf = append(w.Buffer.Buf, '"') 171 w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10) 172 w.Buffer.Buf = append(w.Buffer.Buf, '"') 173} 174 175func (w *Writer) Uint32Str(n uint32) { 176 w.Buffer.EnsureSpace(10) 177 w.Buffer.Buf = append(w.Buffer.Buf, '"') 178 w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10) 179 w.Buffer.Buf = append(w.Buffer.Buf, '"') 180} 181 182func (w *Writer) UintStr(n uint) { 183 w.Buffer.EnsureSpace(20) 184 w.Buffer.Buf = append(w.Buffer.Buf, '"') 185 w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10) 186 w.Buffer.Buf = append(w.Buffer.Buf, '"') 187} 188 189func (w *Writer) Uint64Str(n uint64) { 190 w.Buffer.EnsureSpace(20) 191 w.Buffer.Buf = append(w.Buffer.Buf, '"') 192 w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, n, 10) 193 w.Buffer.Buf = append(w.Buffer.Buf, '"') 194} 195 196func (w *Writer) UintptrStr(n uintptr) { 197 w.Buffer.EnsureSpace(20) 198 w.Buffer.Buf = append(w.Buffer.Buf, '"') 199 w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10) 200 w.Buffer.Buf = append(w.Buffer.Buf, '"') 201} 202 203func (w *Writer) Int8Str(n int8) { 204 w.Buffer.EnsureSpace(4) 205 w.Buffer.Buf = append(w.Buffer.Buf, '"') 206 w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10) 207 w.Buffer.Buf = append(w.Buffer.Buf, '"') 208} 209 210func (w *Writer) Int16Str(n int16) { 211 w.Buffer.EnsureSpace(6) 212 w.Buffer.Buf = append(w.Buffer.Buf, '"') 213 w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10) 214 w.Buffer.Buf = append(w.Buffer.Buf, '"') 215} 216 217func (w *Writer) Int32Str(n int32) { 218 w.Buffer.EnsureSpace(11) 219 w.Buffer.Buf = append(w.Buffer.Buf, '"') 220 w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10) 221 w.Buffer.Buf = append(w.Buffer.Buf, '"') 222} 223 224func (w *Writer) IntStr(n int) { 225 w.Buffer.EnsureSpace(21) 226 w.Buffer.Buf = append(w.Buffer.Buf, '"') 227 w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10) 228 w.Buffer.Buf = append(w.Buffer.Buf, '"') 229} 230 231func (w *Writer) Int64Str(n int64) { 232 w.Buffer.EnsureSpace(21) 233 w.Buffer.Buf = append(w.Buffer.Buf, '"') 234 w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, n, 10) 235 w.Buffer.Buf = append(w.Buffer.Buf, '"') 236} 237 238func (w *Writer) Float32(n float32) { 239 w.Buffer.EnsureSpace(20) 240 w.Buffer.Buf = strconv.AppendFloat(w.Buffer.Buf, float64(n), 'g', -1, 32) 241} 242 243func (w *Writer) Float32Str(n float32) { 244 w.Buffer.EnsureSpace(20) 245 w.Buffer.Buf = append(w.Buffer.Buf, '"') 246 w.Buffer.Buf = strconv.AppendFloat(w.Buffer.Buf, float64(n), 'g', -1, 32) 247 w.Buffer.Buf = append(w.Buffer.Buf, '"') 248} 249 250func (w *Writer) Float64(n float64) { 251 w.Buffer.EnsureSpace(20) 252 w.Buffer.Buf = strconv.AppendFloat(w.Buffer.Buf, n, 'g', -1, 64) 253} 254 255func (w *Writer) Float64Str(n float64) { 256 w.Buffer.EnsureSpace(20) 257 w.Buffer.Buf = append(w.Buffer.Buf, '"') 258 w.Buffer.Buf = strconv.AppendFloat(w.Buffer.Buf, float64(n), 'g', -1, 64) 259 w.Buffer.Buf = append(w.Buffer.Buf, '"') 260} 261 262func (w *Writer) Bool(v bool) { 263 w.Buffer.EnsureSpace(5) 264 if v { 265 w.Buffer.Buf = append(w.Buffer.Buf, "true"...) 266 } else { 267 w.Buffer.Buf = append(w.Buffer.Buf, "false"...) 268 } 269} 270 271const chars = "0123456789abcdef" 272 273func isNotEscapedSingleChar(c byte, escapeHTML bool) bool { 274 // Note: might make sense to use a table if there are more chars to escape. With 4 chars 275 // it benchmarks the same. 276 if escapeHTML { 277 return c != '<' && c != '>' && c != '&' && c != '\\' && c != '"' && c >= 0x20 && c < utf8.RuneSelf 278 } else { 279 return c != '\\' && c != '"' && c >= 0x20 && c < utf8.RuneSelf 280 } 281} 282 283func (w *Writer) String(s string) { 284 w.Buffer.AppendByte('"') 285 286 // Portions of the string that contain no escapes are appended as 287 // byte slices. 288 289 p := 0 // last non-escape symbol 290 291 for i := 0; i < len(s); { 292 c := s[i] 293 294 if isNotEscapedSingleChar(c, !w.NoEscapeHTML) { 295 // single-width character, no escaping is required 296 i++ 297 continue 298 } else if c < utf8.RuneSelf { 299 // single-with character, need to escape 300 w.Buffer.AppendString(s[p:i]) 301 switch c { 302 case '\t': 303 w.Buffer.AppendString(`\t`) 304 case '\r': 305 w.Buffer.AppendString(`\r`) 306 case '\n': 307 w.Buffer.AppendString(`\n`) 308 case '\\': 309 w.Buffer.AppendString(`\\`) 310 case '"': 311 w.Buffer.AppendString(`\"`) 312 default: 313 w.Buffer.AppendString(`\u00`) 314 w.Buffer.AppendByte(chars[c>>4]) 315 w.Buffer.AppendByte(chars[c&0xf]) 316 } 317 318 i++ 319 p = i 320 continue 321 } 322 323 // broken utf 324 runeValue, runeWidth := utf8.DecodeRuneInString(s[i:]) 325 if runeValue == utf8.RuneError && runeWidth == 1 { 326 w.Buffer.AppendString(s[p:i]) 327 w.Buffer.AppendString(`\ufffd`) 328 i++ 329 p = i 330 continue 331 } 332 333 // jsonp stuff - tab separator and line separator 334 if runeValue == '\u2028' || runeValue == '\u2029' { 335 w.Buffer.AppendString(s[p:i]) 336 w.Buffer.AppendString(`\u202`) 337 w.Buffer.AppendByte(chars[runeValue&0xf]) 338 i += runeWidth 339 p = i 340 continue 341 } 342 i += runeWidth 343 } 344 w.Buffer.AppendString(s[p:]) 345 w.Buffer.AppendByte('"') 346} 347 348const encode = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" 349const padChar = '=' 350 351func (w *Writer) base64(in []byte) { 352 353 if len(in) == 0 { 354 return 355 } 356 357 w.Buffer.EnsureSpace(((len(in)-1)/3 + 1) * 4) 358 359 si := 0 360 n := (len(in) / 3) * 3 361 362 for si < n { 363 // Convert 3x 8bit source bytes into 4 bytes 364 val := uint(in[si+0])<<16 | uint(in[si+1])<<8 | uint(in[si+2]) 365 366 w.Buffer.Buf = append(w.Buffer.Buf, encode[val>>18&0x3F], encode[val>>12&0x3F], encode[val>>6&0x3F], encode[val&0x3F]) 367 368 si += 3 369 } 370 371 remain := len(in) - si 372 if remain == 0 { 373 return 374 } 375 376 // Add the remaining small block 377 val := uint(in[si+0]) << 16 378 if remain == 2 { 379 val |= uint(in[si+1]) << 8 380 } 381 382 w.Buffer.Buf = append(w.Buffer.Buf, encode[val>>18&0x3F], encode[val>>12&0x3F]) 383 384 switch remain { 385 case 2: 386 w.Buffer.Buf = append(w.Buffer.Buf, encode[val>>6&0x3F], byte(padChar)) 387 case 1: 388 w.Buffer.Buf = append(w.Buffer.Buf, byte(padChar), byte(padChar)) 389 } 390} 391