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  }