forked from github/multus-cni
gomodule is still in progress to migrate for now, hence multus team decide to keep vendor directory to support build without gomodule.
373 lines
7.8 KiB
Go
373 lines
7.8 KiB
Go
package jsoniter
|
|
|
|
import (
|
|
"unicode/utf8"
|
|
)
|
|
|
|
// htmlSafeSet holds the value true if the ASCII character with the given
|
|
// array position can be safely represented inside a JSON string, embedded
|
|
// inside of HTML <script> tags, without any additional escaping.
|
|
//
|
|
// All values are true except for the ASCII control characters (0-31), the
|
|
// double quote ("), the backslash character ("\"), HTML opening and closing
|
|
// tags ("<" and ">"), and the ampersand ("&").
|
|
var htmlSafeSet = [utf8.RuneSelf]bool{
|
|
' ': true,
|
|
'!': true,
|
|
'"': false,
|
|
'#': true,
|
|
'$': true,
|
|
'%': true,
|
|
'&': false,
|
|
'\'': true,
|
|
'(': true,
|
|
')': true,
|
|
'*': true,
|
|
'+': true,
|
|
',': true,
|
|
'-': true,
|
|
'.': true,
|
|
'/': true,
|
|
'0': true,
|
|
'1': true,
|
|
'2': true,
|
|
'3': true,
|
|
'4': true,
|
|
'5': true,
|
|
'6': true,
|
|
'7': true,
|
|
'8': true,
|
|
'9': true,
|
|
':': true,
|
|
';': true,
|
|
'<': false,
|
|
'=': true,
|
|
'>': false,
|
|
'?': true,
|
|
'@': true,
|
|
'A': true,
|
|
'B': true,
|
|
'C': true,
|
|
'D': true,
|
|
'E': true,
|
|
'F': true,
|
|
'G': true,
|
|
'H': true,
|
|
'I': true,
|
|
'J': true,
|
|
'K': true,
|
|
'L': true,
|
|
'M': true,
|
|
'N': true,
|
|
'O': true,
|
|
'P': true,
|
|
'Q': true,
|
|
'R': true,
|
|
'S': true,
|
|
'T': true,
|
|
'U': true,
|
|
'V': true,
|
|
'W': true,
|
|
'X': true,
|
|
'Y': true,
|
|
'Z': true,
|
|
'[': true,
|
|
'\\': false,
|
|
']': true,
|
|
'^': true,
|
|
'_': true,
|
|
'`': true,
|
|
'a': true,
|
|
'b': true,
|
|
'c': true,
|
|
'd': true,
|
|
'e': true,
|
|
'f': true,
|
|
'g': true,
|
|
'h': true,
|
|
'i': true,
|
|
'j': true,
|
|
'k': true,
|
|
'l': true,
|
|
'm': true,
|
|
'n': true,
|
|
'o': true,
|
|
'p': true,
|
|
'q': true,
|
|
'r': true,
|
|
's': true,
|
|
't': true,
|
|
'u': true,
|
|
'v': true,
|
|
'w': true,
|
|
'x': true,
|
|
'y': true,
|
|
'z': true,
|
|
'{': true,
|
|
'|': true,
|
|
'}': true,
|
|
'~': true,
|
|
'\u007f': true,
|
|
}
|
|
|
|
// safeSet holds the value true if the ASCII character with the given array
|
|
// position can be represented inside a JSON string without any further
|
|
// escaping.
|
|
//
|
|
// All values are true except for the ASCII control characters (0-31), the
|
|
// double quote ("), and the backslash character ("\").
|
|
var safeSet = [utf8.RuneSelf]bool{
|
|
' ': true,
|
|
'!': true,
|
|
'"': false,
|
|
'#': true,
|
|
'$': true,
|
|
'%': true,
|
|
'&': true,
|
|
'\'': true,
|
|
'(': true,
|
|
')': true,
|
|
'*': true,
|
|
'+': true,
|
|
',': true,
|
|
'-': true,
|
|
'.': true,
|
|
'/': true,
|
|
'0': true,
|
|
'1': true,
|
|
'2': true,
|
|
'3': true,
|
|
'4': true,
|
|
'5': true,
|
|
'6': true,
|
|
'7': true,
|
|
'8': true,
|
|
'9': true,
|
|
':': true,
|
|
';': true,
|
|
'<': true,
|
|
'=': true,
|
|
'>': true,
|
|
'?': true,
|
|
'@': true,
|
|
'A': true,
|
|
'B': true,
|
|
'C': true,
|
|
'D': true,
|
|
'E': true,
|
|
'F': true,
|
|
'G': true,
|
|
'H': true,
|
|
'I': true,
|
|
'J': true,
|
|
'K': true,
|
|
'L': true,
|
|
'M': true,
|
|
'N': true,
|
|
'O': true,
|
|
'P': true,
|
|
'Q': true,
|
|
'R': true,
|
|
'S': true,
|
|
'T': true,
|
|
'U': true,
|
|
'V': true,
|
|
'W': true,
|
|
'X': true,
|
|
'Y': true,
|
|
'Z': true,
|
|
'[': true,
|
|
'\\': false,
|
|
']': true,
|
|
'^': true,
|
|
'_': true,
|
|
'`': true,
|
|
'a': true,
|
|
'b': true,
|
|
'c': true,
|
|
'd': true,
|
|
'e': true,
|
|
'f': true,
|
|
'g': true,
|
|
'h': true,
|
|
'i': true,
|
|
'j': true,
|
|
'k': true,
|
|
'l': true,
|
|
'm': true,
|
|
'n': true,
|
|
'o': true,
|
|
'p': true,
|
|
'q': true,
|
|
'r': true,
|
|
's': true,
|
|
't': true,
|
|
'u': true,
|
|
'v': true,
|
|
'w': true,
|
|
'x': true,
|
|
'y': true,
|
|
'z': true,
|
|
'{': true,
|
|
'|': true,
|
|
'}': true,
|
|
'~': true,
|
|
'\u007f': true,
|
|
}
|
|
|
|
var hex = "0123456789abcdef"
|
|
|
|
// WriteStringWithHTMLEscaped write string to stream with html special characters escaped
|
|
func (stream *Stream) WriteStringWithHTMLEscaped(s string) {
|
|
valLen := len(s)
|
|
stream.buf = append(stream.buf, '"')
|
|
// write string, the fast path, without utf8 and escape support
|
|
i := 0
|
|
for ; i < valLen; i++ {
|
|
c := s[i]
|
|
if c < utf8.RuneSelf && htmlSafeSet[c] {
|
|
stream.buf = append(stream.buf, c)
|
|
} else {
|
|
break
|
|
}
|
|
}
|
|
if i == valLen {
|
|
stream.buf = append(stream.buf, '"')
|
|
return
|
|
}
|
|
writeStringSlowPathWithHTMLEscaped(stream, i, s, valLen)
|
|
}
|
|
|
|
func writeStringSlowPathWithHTMLEscaped(stream *Stream, i int, s string, valLen int) {
|
|
start := i
|
|
// for the remaining parts, we process them char by char
|
|
for i < valLen {
|
|
if b := s[i]; b < utf8.RuneSelf {
|
|
if htmlSafeSet[b] {
|
|
i++
|
|
continue
|
|
}
|
|
if start < i {
|
|
stream.WriteRaw(s[start:i])
|
|
}
|
|
switch b {
|
|
case '\\', '"':
|
|
stream.writeTwoBytes('\\', b)
|
|
case '\n':
|
|
stream.writeTwoBytes('\\', 'n')
|
|
case '\r':
|
|
stream.writeTwoBytes('\\', 'r')
|
|
case '\t':
|
|
stream.writeTwoBytes('\\', 't')
|
|
default:
|
|
// This encodes bytes < 0x20 except for \t, \n and \r.
|
|
// If escapeHTML is set, it also escapes <, >, and &
|
|
// because they can lead to security holes when
|
|
// user-controlled strings are rendered into JSON
|
|
// and served to some browsers.
|
|
stream.WriteRaw(`\u00`)
|
|
stream.writeTwoBytes(hex[b>>4], hex[b&0xF])
|
|
}
|
|
i++
|
|
start = i
|
|
continue
|
|
}
|
|
c, size := utf8.DecodeRuneInString(s[i:])
|
|
if c == utf8.RuneError && size == 1 {
|
|
if start < i {
|
|
stream.WriteRaw(s[start:i])
|
|
}
|
|
stream.WriteRaw(`\ufffd`)
|
|
i++
|
|
start = i
|
|
continue
|
|
}
|
|
// U+2028 is LINE SEPARATOR.
|
|
// U+2029 is PARAGRAPH SEPARATOR.
|
|
// They are both technically valid characters in JSON strings,
|
|
// but don't work in JSONP, which has to be evaluated as JavaScript,
|
|
// and can lead to security holes there. It is valid JSON to
|
|
// escape them, so we do so unconditionally.
|
|
// See http://timelessrepo.com/json-isnt-a-javascript-subset for discussion.
|
|
if c == '\u2028' || c == '\u2029' {
|
|
if start < i {
|
|
stream.WriteRaw(s[start:i])
|
|
}
|
|
stream.WriteRaw(`\u202`)
|
|
stream.writeByte(hex[c&0xF])
|
|
i += size
|
|
start = i
|
|
continue
|
|
}
|
|
i += size
|
|
}
|
|
if start < len(s) {
|
|
stream.WriteRaw(s[start:])
|
|
}
|
|
stream.writeByte('"')
|
|
}
|
|
|
|
// WriteString write string to stream without html escape
|
|
func (stream *Stream) WriteString(s string) {
|
|
valLen := len(s)
|
|
stream.buf = append(stream.buf, '"')
|
|
// write string, the fast path, without utf8 and escape support
|
|
i := 0
|
|
for ; i < valLen; i++ {
|
|
c := s[i]
|
|
if c > 31 && c != '"' && c != '\\' {
|
|
stream.buf = append(stream.buf, c)
|
|
} else {
|
|
break
|
|
}
|
|
}
|
|
if i == valLen {
|
|
stream.buf = append(stream.buf, '"')
|
|
return
|
|
}
|
|
writeStringSlowPath(stream, i, s, valLen)
|
|
}
|
|
|
|
func writeStringSlowPath(stream *Stream, i int, s string, valLen int) {
|
|
start := i
|
|
// for the remaining parts, we process them char by char
|
|
for i < valLen {
|
|
if b := s[i]; b < utf8.RuneSelf {
|
|
if safeSet[b] {
|
|
i++
|
|
continue
|
|
}
|
|
if start < i {
|
|
stream.WriteRaw(s[start:i])
|
|
}
|
|
switch b {
|
|
case '\\', '"':
|
|
stream.writeTwoBytes('\\', b)
|
|
case '\n':
|
|
stream.writeTwoBytes('\\', 'n')
|
|
case '\r':
|
|
stream.writeTwoBytes('\\', 'r')
|
|
case '\t':
|
|
stream.writeTwoBytes('\\', 't')
|
|
default:
|
|
// This encodes bytes < 0x20 except for \t, \n and \r.
|
|
// If escapeHTML is set, it also escapes <, >, and &
|
|
// because they can lead to security holes when
|
|
// user-controlled strings are rendered into JSON
|
|
// and served to some browsers.
|
|
stream.WriteRaw(`\u00`)
|
|
stream.writeTwoBytes(hex[b>>4], hex[b&0xF])
|
|
}
|
|
i++
|
|
start = i
|
|
continue
|
|
}
|
|
i++
|
|
continue
|
|
}
|
|
if start < len(s) {
|
|
stream.WriteRaw(s[start:])
|
|
}
|
|
stream.writeByte('"')
|
|
}
|