1package 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} 122