github.com/inspektor-gadget/inspektor-gadget@v0.28.1/pkg/datasource/formatters/json/json.go (about) 1 // Copyright 2024 The Inspektor Gadget authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package json 16 17 import ( 18 "fmt" 19 "math" 20 "slices" 21 "strconv" 22 "strings" 23 "unicode/utf8" 24 _ "unsafe" 25 26 "github.com/inspektor-gadget/inspektor-gadget/pkg/datasource" 27 "github.com/inspektor-gadget/inspektor-gadget/pkg/gadget-service/api" 28 ) 29 30 var ( 31 opener = []byte("{") 32 closer = []byte("}") 33 fieldSep = []byte(",") 34 fieldSepPretty = []byte(",\n") 35 openerPretty = []byte("{\n") 36 ) 37 38 type Formatter struct { 39 ds datasource.DataSource 40 fns []func(e *encodeState, data datasource.Data) 41 fields []string 42 showFields map[string]struct{} 43 hideFields map[string]struct{} 44 allRelativeFields bool 45 useDefault bool 46 showAll bool 47 pretty bool 48 indent string 49 opener []byte 50 fieldSep []byte 51 } 52 53 func New(ds datasource.DataSource, options ...Option) (*Formatter, error) { 54 f := &Formatter{ 55 ds: ds, 56 showFields: map[string]struct{}{}, 57 hideFields: map[string]struct{}{}, 58 useDefault: true, 59 } 60 for _, o := range options { 61 o(f) 62 } 63 err := f.init() 64 if err != nil { 65 return nil, err 66 } 67 return f, nil 68 } 69 70 func (f *Formatter) init() error { 71 f.opener = opener 72 f.fieldSep = fieldSep 73 if f.pretty { 74 f.opener = openerPretty 75 f.fieldSep = fieldSepPretty 76 } 77 for _, field := range f.fields { 78 if len(field) == 0 { 79 continue 80 } 81 switch field[0] { 82 case '+': 83 if _, ok := f.hideFields[field[1:]]; ok { 84 return fmt.Errorf("field %q both added (+) and removed (-)", field[1:]) 85 } 86 f.showFields[field[1:]] = struct{}{} 87 case '-': 88 if _, ok := f.showFields[field[1:]]; ok { 89 return fmt.Errorf("field %q both added (+) and removed (-)", field[1:]) 90 } 91 f.hideFields[field[1:]] = struct{}{} 92 default: 93 f.showFields[field] = struct{}{} 94 f.allRelativeFields = false 95 } 96 } 97 98 closer := closer 99 if f.pretty { 100 closer = append([]byte("\n"), closer...) 101 } 102 103 f.fns = append(f.fns, func(e *encodeState, data datasource.Data) { 104 e.Write(f.opener) 105 }) 106 subFieldFuncs, _ := f.addSubFields(nil, "", f.indent) 107 f.fns = append(f.fns, subFieldFuncs...) 108 f.fns = append(f.fns, func(e *encodeState, data datasource.Data) { 109 e.Write(closer) 110 }) 111 return nil 112 } 113 114 func (f *Formatter) addSubFields(accessors []datasource.FieldAccessor, prefix string, indent string) (fns []func(*encodeState, datasource.Data), fieldCounter int) { 115 if accessors == nil { 116 accessors = f.ds.Accessors(true) 117 } 118 119 ctr := -1 120 121 // sort lexicographically 122 slices.SortFunc(accessors, func(i datasource.FieldAccessor, j datasource.FieldAccessor) int { 123 return strings.Compare(i.Name(), j.Name()) 124 }) 125 126 for _, acc := range accessors { 127 accessor := acc 128 129 // skip unreferenced fields 130 if datasource.FieldFlagUnreferenced.In(accessor.Flags()) { 131 continue 132 } 133 134 fullFieldName := prefix + accessor.Name() 135 136 var subFieldFuncs []func(state *encodeState, data datasource.Data) 137 var subFieldCount int 138 subFields := accessor.SubFields() 139 if len(subFields) > 0 { 140 subFieldFuncs, subFieldCount = f.addSubFields(subFields, fullFieldName+".", indent+f.indent) 141 fieldCounter += subFieldCount 142 } 143 144 // If subFieldCount is > 0, a child of this field has been requested, so we also 145 // need to show this parent; if not, we follow the default rules of field visibility 146 if subFieldCount == 0 { 147 if !f.useDefault { 148 if _, ok := f.hideFields[fullFieldName]; ok { 149 continue 150 } 151 if _, ok := f.showFields[fullFieldName]; !ok { 152 if !f.allRelativeFields { 153 continue 154 } 155 if datasource.FieldFlagHidden.In(accessor.Flags()) { 156 continue 157 } 158 } 159 } else { 160 if !f.showAll && datasource.FieldFlagHidden.In(accessor.Flags()) { 161 continue 162 } 163 } 164 } 165 166 ctr++ 167 fieldCounter++ 168 fieldName := []byte("\"" + accessor.Name() + "\":") 169 if f.pretty { 170 fieldName = append(append([]byte(indent), fieldName...), ' ') 171 } 172 if ctr > 0 { 173 fns = append(fns, func(e *encodeState, data datasource.Data) { 174 e.Write(f.fieldSep) 175 }) 176 } 177 178 closer := closer 179 if f.pretty { 180 closer = append([]byte("\n"+indent), closer...) 181 } 182 183 // Field has subfields 184 if len(subFields) > 0 { 185 fns = append(fns, func(e *encodeState, data datasource.Data) { 186 e.Write(fieldName) 187 e.Write(f.opener) 188 }) 189 fns = append(fns, subFieldFuncs...) 190 fns = append(fns, func(e *encodeState, data datasource.Data) { 191 e.Write(closer) 192 }) 193 continue 194 } 195 196 var fn func(e *encodeState, data datasource.Data) 197 // Field doesn't have subfields 198 switch accessor.Type() { 199 case api.Kind_Int8: 200 fn = func(e *encodeState, data datasource.Data) { 201 b := strconv.AppendInt(e.scratch[:0], int64(accessor.Int8(data)), 10) 202 e.Write(b) 203 } 204 case api.Kind_Int16: 205 fn = func(e *encodeState, data datasource.Data) { 206 b := strconv.AppendInt(e.scratch[:0], int64(accessor.Int16(data)), 10) 207 e.Write(b) 208 } 209 case api.Kind_Int32: 210 fn = func(e *encodeState, data datasource.Data) { 211 b := strconv.AppendInt(e.scratch[:0], int64(accessor.Int32(data)), 10) 212 e.Write(b) 213 } 214 case api.Kind_Int64: 215 fn = func(e *encodeState, data datasource.Data) { 216 b := strconv.AppendInt(e.scratch[:0], accessor.Int64(data), 10) 217 e.Write(b) 218 } 219 case api.Kind_Uint8: 220 fn = func(e *encodeState, data datasource.Data) { 221 b := strconv.AppendUint(e.scratch[:0], uint64(accessor.Uint8(data)), 10) 222 e.Write(b) 223 } 224 case api.Kind_Uint16: 225 fn = func(e *encodeState, data datasource.Data) { 226 b := strconv.AppendUint(e.scratch[:0], uint64(accessor.Uint16(data)), 10) 227 e.Write(b) 228 } 229 case api.Kind_Uint32: 230 fn = func(e *encodeState, data datasource.Data) { 231 b := strconv.AppendUint(e.scratch[:0], uint64(accessor.Uint32(data)), 10) 232 e.Write(b) 233 } 234 case api.Kind_Uint64: 235 fn = func(e *encodeState, data datasource.Data) { 236 b := strconv.AppendUint(e.scratch[:0], accessor.Uint64(data), 10) 237 e.Write(b) 238 } 239 case api.Kind_Float32: 240 fn = func(e *encodeState, data datasource.Data) { 241 floatEncoder(32).writeFloat(e, float64(accessor.Float32(data))) 242 } 243 case api.Kind_Float64: 244 fn = func(e *encodeState, data datasource.Data) { 245 floatEncoder(64).writeFloat(e, accessor.Float64(data)) 246 } 247 case api.Kind_String: 248 fn = func(e *encodeState, data datasource.Data) { 249 writeString(e, string(accessor.Get(data))) 250 } 251 case api.Kind_Bool: 252 fn = func(e *encodeState, data datasource.Data) { 253 // handle arbitrary length bools 254 for b := range accessor.Get(data) { 255 if b != 0 { 256 e.WriteString("true") 257 return 258 } 259 } 260 e.WriteString("false") 261 } 262 default: 263 fn = func(e *encodeState, data datasource.Data) { 264 writeString(e, accessor.CString(data)) 265 } 266 } 267 fns = append(fns, func(e *encodeState, data datasource.Data) { 268 e.Write(fieldName) 269 fn(e, data) 270 }) 271 } 272 return 273 } 274 275 func (f *Formatter) Marshal(data datasource.Data) []byte { 276 e := bufpool.Get().(*encodeState) 277 e.Reset() 278 defer bufpool.Put(e) 279 for _, fn := range f.fns { 280 fn(e, data) 281 } 282 return e.Bytes() 283 } 284 285 type floatEncoder int // number of bits 286 287 // from encoding/json/encode.go 288 func (bits floatEncoder) writeFloat(e *encodeState, f float64) { 289 if math.IsInf(f, 0) || math.IsNaN(f) { 290 e.err = fmt.Errorf("invalid float value") 291 return 292 } 293 294 // Convert as if by ES6 number to string conversion. 295 // This matches most other JSON generators. 296 // See golang.org/issue/6384 and golang.org/issue/14135. 297 // Like fmt %g, but the exponent cutoffs are different 298 // and exponents themselves are not padded to two digits. 299 b := e.scratch[:0] 300 abs := math.Abs(f) 301 fmt := byte('f') 302 // Note: Must use float32 comparisons for underlying float32 value to get precise cutoffs right. 303 if abs != 0 { 304 if bits == 64 && (abs < 1e-6 || abs >= 1e21) || bits == 32 && (float32(abs) < 1e-6 || float32(abs) >= 1e21) { 305 fmt = 'e' 306 } 307 } 308 b = strconv.AppendFloat(b, f, fmt, -1, int(bits)) 309 if fmt == 'e' { 310 // clean up e-09 to e-9 311 n := len(b) 312 if n >= 4 && b[n-4] == 'e' && b[n-3] == '-' && b[n-2] == '0' { 313 b[n-2] = b[n-1] 314 b = b[:n-1] 315 } 316 } 317 318 e.Write(b) 319 } 320 321 // from encoding/json/encode.go 322 func writeString(e *encodeState, s string) { 323 e.WriteByte('"') 324 start := 0 325 for i := 0; i < len(s); { 326 if b := s[i]; b < utf8.RuneSelf { 327 if safeSet[b] { 328 i++ 329 continue 330 } 331 if start < i { 332 e.WriteString(s[start:i]) 333 } 334 e.WriteByte('\\') 335 switch b { 336 case '\\', '"': 337 e.WriteByte(b) 338 case '\n': 339 e.WriteByte('n') 340 case '\r': 341 e.WriteByte('r') 342 case '\t': 343 e.WriteByte('t') 344 default: 345 // This encodes bytes < 0x20 except for \t, \n and \r. 346 // If escapeHTML is set, it also escapes <, >, and & 347 // because they can lead to security holes when 348 // user-controlled strings are rendered into JSON 349 // and served to some browsers. 350 e.WriteString(`u00`) 351 e.WriteByte(hex[b>>4]) 352 e.WriteByte(hex[b&0xF]) 353 } 354 i++ 355 start = i 356 continue 357 } 358 c, size := utf8.DecodeRuneInString(s[i:]) 359 if c == utf8.RuneError && size == 1 { 360 if start < i { 361 e.WriteString(s[start:i]) 362 } 363 e.WriteString(`\ufffd`) 364 i += size 365 start = i 366 continue 367 } 368 // U+2028 is LINE SEPARATOR. 369 // U+2029 is PARAGRAPH SEPARATOR. 370 // They are both technically valid characters in JSON strings, 371 // but don't work in JSONP, which has to be evaluated as JavaScript, 372 // and can lead to security holes there. It is valid JSON to 373 // escape them, so we do so unconditionally. 374 // See http://timelessrepo.com/json-isnt-a-javascript-subset for discussion. 375 if c == '\u2028' || c == '\u2029' { 376 if start < i { 377 e.WriteString(s[start:i]) 378 } 379 e.WriteString(`\u202`) 380 e.WriteByte(hex[c&0xF]) 381 i += size 382 start = i 383 continue 384 } 385 i += size 386 } 387 if start < len(s) { 388 e.WriteString(s[start:]) 389 } 390 e.WriteByte('"') 391 } 392 393 var hex = "0123456789abcdef" 394 395 // use safeSet from encoding/json directly 396 // 397 //go:linkname safeSet encoding/json.safeSet 398 var safeSet = [utf8.RuneSelf]bool{}