blob: 815906ff7070c42ff18f6e7dd93aa6afd9d7d096 [file] [log] [blame]
khenaidooac637102019-01-14 15:44:34 -05001package json
2
3import "unicode/utf8"
4
5const hex = "0123456789abcdef"
6
7var noEscapeTable = [256]bool{}
8
9func init() {
10 for i := 0; i <= 0x7e; i++ {
11 noEscapeTable[i] = i >= 0x20 && i != '\\' && i != '"'
12 }
13}
14
15// AppendStrings encodes the input strings to json and
16// appends the encoded string list to the input byte slice.
17func (e Encoder) AppendStrings(dst []byte, vals []string) []byte {
18 if len(vals) == 0 {
19 return append(dst, '[', ']')
20 }
21 dst = append(dst, '[')
22 dst = e.AppendString(dst, vals[0])
23 if len(vals) > 1 {
24 for _, val := range vals[1:] {
25 dst = e.AppendString(append(dst, ','), val)
26 }
27 }
28 dst = append(dst, ']')
29 return dst
30}
31
32// AppendString encodes the input string to json and appends
33// the encoded string to the input byte slice.
34//
35// The operation loops though each byte in the string looking
36// for characters that need json or utf8 encoding. If the string
37// does not need encoding, then the string is appended in it's
38// entirety to the byte slice.
39// If we encounter a byte that does need encoding, switch up
40// the operation and perform a byte-by-byte read-encode-append.
41func (Encoder) AppendString(dst []byte, s string) []byte {
42 // Start with a double quote.
43 dst = append(dst, '"')
44 // Loop through each character in the string.
45 for i := 0; i < len(s); i++ {
46 // Check if the character needs encoding. Control characters, slashes,
47 // and the double quote need json encoding. Bytes above the ascii
48 // boundary needs utf8 encoding.
49 if !noEscapeTable[s[i]] {
50 // We encountered a character that needs to be encoded. Switch
51 // to complex version of the algorithm.
52 dst = appendStringComplex(dst, s, i)
53 return append(dst, '"')
54 }
55 }
56 // The string has no need for encoding an therefore is directly
57 // appended to the byte slice.
58 dst = append(dst, s...)
59 // End with a double quote
60 return append(dst, '"')
61}
62
63// appendStringComplex is used by appendString to take over an in
64// progress JSON string encoding that encountered a character that needs
65// to be encoded.
66func appendStringComplex(dst []byte, s string, i int) []byte {
67 start := 0
68 for i < len(s) {
69 b := s[i]
70 if b >= utf8.RuneSelf {
71 r, size := utf8.DecodeRuneInString(s[i:])
72 if r == utf8.RuneError && size == 1 {
73 // In case of error, first append previous simple characters to
74 // the byte slice if any and append a remplacement character code
75 // in place of the invalid sequence.
76 if start < i {
77 dst = append(dst, s[start:i]...)
78 }
79 dst = append(dst, `\ufffd`...)
80 i += size
81 start = i
82 continue
83 }
84 i += size
85 continue
86 }
87 if noEscapeTable[b] {
88 i++
89 continue
90 }
91 // We encountered a character that needs to be encoded.
92 // Let's append the previous simple characters to the byte slice
93 // and switch our operation to read and encode the remainder
94 // characters byte-by-byte.
95 if start < i {
96 dst = append(dst, s[start:i]...)
97 }
98 switch b {
99 case '"', '\\':
100 dst = append(dst, '\\', b)
101 case '\b':
102 dst = append(dst, '\\', 'b')
103 case '\f':
104 dst = append(dst, '\\', 'f')
105 case '\n':
106 dst = append(dst, '\\', 'n')
107 case '\r':
108 dst = append(dst, '\\', 'r')
109 case '\t':
110 dst = append(dst, '\\', 't')
111 default:
112 dst = append(dst, '\\', 'u', '0', '0', hex[b>>4], hex[b&0xF])
113 }
114 i++
115 start = i
116 }
117 if start < len(s) {
118 dst = append(dst, s[start:]...)
119 }
120 return dst
121}