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{}