github.com/v2pro/plz@v0.0.0-20221028024117-e5f9aec5b631/msgfmt/jsonfmt/encoder_str.go (about) 1 package jsonfmt 2 3 import ( 4 "unsafe" 5 "unicode/utf8" 6 "context" 7 ) 8 9 // safeSet holds the value true if the ASCII character with the given array 10 // position can be represented inside a JSON string without any further 11 // escaping. 12 // 13 // All values are true except for the ASCII control characters (0-31), the 14 // double quote ("), and the backslash character ("\"). 15 var safeSet = [utf8.RuneSelf]bool{ 16 ' ': true, 17 '!': true, 18 '"': false, 19 '#': true, 20 '$': true, 21 '%': true, 22 '&': true, 23 '\'': true, 24 '(': true, 25 ')': true, 26 '*': true, 27 '+': true, 28 ',': true, 29 '-': true, 30 '.': true, 31 '/': true, 32 '0': true, 33 '1': true, 34 '2': true, 35 '3': true, 36 '4': true, 37 '5': true, 38 '6': true, 39 '7': true, 40 '8': true, 41 '9': true, 42 ':': true, 43 ';': true, 44 '<': true, 45 '=': true, 46 '>': true, 47 '?': true, 48 '@': true, 49 'A': true, 50 'B': true, 51 'C': true, 52 'D': true, 53 'E': true, 54 'F': true, 55 'G': true, 56 'H': true, 57 'I': true, 58 'J': true, 59 'K': true, 60 'L': true, 61 'M': true, 62 'N': true, 63 'O': true, 64 'P': true, 65 'Q': true, 66 'R': true, 67 'S': true, 68 'T': true, 69 'U': true, 70 'V': true, 71 'W': true, 72 'X': true, 73 'Y': true, 74 'Z': true, 75 '[': true, 76 '\\': false, 77 ']': true, 78 '^': true, 79 '_': true, 80 '`': true, 81 'a': true, 82 'b': true, 83 'c': true, 84 'd': true, 85 'e': true, 86 'f': true, 87 'g': true, 88 'h': true, 89 'i': true, 90 'j': true, 91 'k': true, 92 'l': true, 93 'm': true, 94 'n': true, 95 'o': true, 96 'p': true, 97 'q': true, 98 'r': true, 99 's': true, 100 't': true, 101 'u': true, 102 'v': true, 103 'w': true, 104 'x': true, 105 'y': true, 106 'z': true, 107 '{': true, 108 '|': true, 109 '}': true, 110 '~': true, 111 '\u007f': true, 112 } 113 114 var hex = "0123456789abcdef" 115 116 type stringEncoder struct { 117 } 118 119 func (encoder *stringEncoder) Encode(ctx context.Context, space []byte, ptr unsafe.Pointer) []byte { 120 return WriteString(space, *(*string)(ptr)) 121 } 122 123 func WriteString(space []byte, str string) []byte { 124 space = append(space, '"') 125 // write string, the fast path, without utf8 and escape support 126 var i int 127 var c byte 128 for i, c = range []byte(str) { 129 if c > 31 && c != '"' && c != '\\' { 130 space = append(space, c) 131 } else { 132 break 133 } 134 } 135 if i == len(str)-1 { 136 space = append(space, '"') 137 return space 138 } 139 return writeStringSlowPath(space, []byte(str[i:])) 140 } 141 142 func writeStringSlowPath(space []byte, s []byte) []byte { 143 start := 0 144 // for the remaining parts, we process them char by char 145 var i int 146 var b byte 147 for i, b = range s { 148 if b >= utf8.RuneSelf { 149 continue 150 } 151 if safeSet[b] { 152 continue 153 } 154 if start < i { 155 space = append(space, s[start:i]...) 156 } 157 switch b { 158 case '\\', '"': 159 space = append(space, '\\', b) 160 case '\n': 161 space = append(space, '\\', 'n') 162 case '\r': 163 space = append(space, '\\', 'r') 164 case '\t': 165 space = append(space, '\\', 't') 166 default: 167 // This encodes bytes < 0x20 except for \t, \n and \r. 168 space = append(space, '\\', 'u', '0', '0', hex[b>>4], hex[b&0xF]) 169 } 170 start = i + 1 171 } 172 if start < len(s) { 173 space = append(space, s[start:]...) 174 } 175 return append(space, '"') 176 }