github.com/lulzWill/go-agent@v2.1.2+incompatible/internal/jsonx/encode.go (about) 1 // Copyright 2010 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 jsonx extends the encoding/json package to encode JSON 6 // incrementally and without requiring reflection. 7 package jsonx 8 9 import ( 10 "bytes" 11 "encoding/json" 12 "math" 13 "reflect" 14 "strconv" 15 "unicode/utf8" 16 ) 17 18 var hex = "0123456789abcdef" 19 20 // AppendString escapes s appends it to buf. 21 func AppendString(buf *bytes.Buffer, s string) { 22 buf.WriteByte('"') 23 start := 0 24 for i := 0; i < len(s); { 25 if b := s[i]; b < utf8.RuneSelf { 26 if 0x20 <= b && b != '\\' && b != '"' && b != '<' && b != '>' && b != '&' { 27 i++ 28 continue 29 } 30 if start < i { 31 buf.WriteString(s[start:i]) 32 } 33 switch b { 34 case '\\', '"': 35 buf.WriteByte('\\') 36 buf.WriteByte(b) 37 case '\n': 38 buf.WriteByte('\\') 39 buf.WriteByte('n') 40 case '\r': 41 buf.WriteByte('\\') 42 buf.WriteByte('r') 43 case '\t': 44 buf.WriteByte('\\') 45 buf.WriteByte('t') 46 default: 47 // This encodes bytes < 0x20 except for \n and \r, 48 // as well as <, > and &. The latter are escaped because they 49 // can lead to security holes when user-controlled strings 50 // are rendered into JSON and served to some browsers. 51 buf.WriteString(`\u00`) 52 buf.WriteByte(hex[b>>4]) 53 buf.WriteByte(hex[b&0xF]) 54 } 55 i++ 56 start = i 57 continue 58 } 59 c, size := utf8.DecodeRuneInString(s[i:]) 60 if c == utf8.RuneError && size == 1 { 61 if start < i { 62 buf.WriteString(s[start:i]) 63 } 64 buf.WriteString(`\ufffd`) 65 i += size 66 start = i 67 continue 68 } 69 // U+2028 is LINE SEPARATOR. 70 // U+2029 is PARAGRAPH SEPARATOR. 71 // They are both technically valid characters in JSON strings, 72 // but don't work in JSONP, which has to be evaluated as JavaScript, 73 // and can lead to security holes there. It is valid JSON to 74 // escape them, so we do so unconditionally. 75 // See http://timelessrepo.com/json-isnt-a-javascript-subset for discussion. 76 if c == '\u2028' || c == '\u2029' { 77 if start < i { 78 buf.WriteString(s[start:i]) 79 } 80 buf.WriteString(`\u202`) 81 buf.WriteByte(hex[c&0xF]) 82 i += size 83 start = i 84 continue 85 } 86 i += size 87 } 88 if start < len(s) { 89 buf.WriteString(s[start:]) 90 } 91 buf.WriteByte('"') 92 } 93 94 // AppendStringArray appends an array of string literals to buf. 95 func AppendStringArray(buf *bytes.Buffer, a ...string) { 96 buf.WriteByte('[') 97 for i, s := range a { 98 if i > 0 { 99 buf.WriteByte(',') 100 } 101 AppendString(buf, s) 102 } 103 buf.WriteByte(']') 104 } 105 106 // AppendFloat appends a numeric literal representing the value to buf. 107 func AppendFloat(buf *bytes.Buffer, x float64) error { 108 var scratch [64]byte 109 110 if math.IsInf(x, 0) || math.IsNaN(x) { 111 return &json.UnsupportedValueError{ 112 Value: reflect.ValueOf(x), 113 Str: strconv.FormatFloat(x, 'g', -1, 64), 114 } 115 } 116 117 buf.Write(strconv.AppendFloat(scratch[:0], x, 'g', -1, 64)) 118 return nil 119 } 120 121 // AppendFloatArray appends an array of numeric literals to buf. 122 func AppendFloatArray(buf *bytes.Buffer, a ...float64) error { 123 buf.WriteByte('[') 124 for i, x := range a { 125 if i > 0 { 126 buf.WriteByte(',') 127 } 128 if err := AppendFloat(buf, x); err != nil { 129 return err 130 } 131 } 132 buf.WriteByte(']') 133 return nil 134 } 135 136 // AppendInt appends a numeric literal representing the value to buf. 137 func AppendInt(buf *bytes.Buffer, x int64) { 138 var scratch [64]byte 139 buf.Write(strconv.AppendInt(scratch[:0], x, 10)) 140 } 141 142 // AppendIntArray appends an array of numeric literals to buf. 143 func AppendIntArray(buf *bytes.Buffer, a ...int64) { 144 var scratch [64]byte 145 146 buf.WriteByte('[') 147 for i, x := range a { 148 if i > 0 { 149 buf.WriteByte(',') 150 } 151 buf.Write(strconv.AppendInt(scratch[:0], x, 10)) 152 } 153 buf.WriteByte(']') 154 } 155 156 // AppendUint appends a numeric literal representing the value to buf. 157 func AppendUint(buf *bytes.Buffer, x uint64) { 158 var scratch [64]byte 159 buf.Write(strconv.AppendUint(scratch[:0], x, 10)) 160 } 161 162 // AppendUintArray appends an array of numeric literals to buf. 163 func AppendUintArray(buf *bytes.Buffer, a ...uint64) { 164 var scratch [64]byte 165 166 buf.WriteByte('[') 167 for i, x := range a { 168 if i > 0 { 169 buf.WriteByte(',') 170 } 171 buf.Write(strconv.AppendUint(scratch[:0], x, 10)) 172 } 173 buf.WriteByte(']') 174 }