-
Notifications
You must be signed in to change notification settings - Fork 0
/
type_string.go
111 lines (104 loc) · 2.87 KB
/
type_string.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
package glog
import (
"unicode/utf8"
"github.com/yu31/glog/pkg/buffer"
)
const hex = "0123456789abcdef"
var noEscapeTable = [256]bool{}
func init() {
for i := 0; i <= 0x7e; i++ {
noEscapeTable[i] = i >= 0x20 && i != '\\' && i != '"'
}
}
// AppendStringEscape encodes the input string to buffer.Buffer
//
// The operation loops though each byte in the string looking
// for characters that need json or utf8 encoding. If the string
// does not need encoding, then the string is appended in it's
// entirety to the byte slice.
// If we encounter a byte that does need encoding, switch up
// the operation and perform a byte-by-byte read-encode-append.
func AppendStringEscape(buf *buffer.Buffer, s string) {
// Loop through each character in the string.
for i := 0; i < len(s); i++ {
// Check if the character needs encoding. Control characters, slashes,
// and the double quote need json encoding. Bytes above the ascii
// boundary needs utf8 encoding.
if !noEscapeTable[s[i]] {
// We encountered a character that needs to be encoded. Switch
// to complex version of the algorithm.
appendStringComplex(buf, s, i)
return
}
}
// The string has no need for encoding an therefore is directly
// appended to the byte slice.
buf.AppendString(s)
}
// appendStringComplex is used by appendString to take over an in
// progress JSON string encoding that encountered a character that needs
// to be encoded.
func appendStringComplex(buf *buffer.Buffer, s string, i int) {
start := 0
for i < len(s) {
b := s[i]
if b >= utf8.RuneSelf {
r, size := utf8.DecodeRuneInString(s[i:])
if r == utf8.RuneError && size == 1 {
// In case of error, first append previous simple characters to
// the byte slice if any and append a remplacement character code
// in place of the invalid sequence.
if start < i {
buf.AppendString(s[start:i])
}
buf.AppendString(`\ufffd`)
i += size
start = i
continue
}
i += size
continue
}
if noEscapeTable[b] {
i++
continue
}
// We encountered a character that needs to be encoded.
// Let's append the previous simple characters to the byte slice
// and switch our operation to read and encode the remainder
// characters byte-by-byte.
if start < i {
buf.AppendString(s[start:i])
}
switch b {
case '"', '\\':
buf.AppendByte('\\')
buf.AppendByte(b)
case '\b':
buf.AppendByte('\\')
buf.AppendByte('b')
case '\f':
buf.AppendByte('\\')
buf.AppendByte('f')
case '\n':
buf.AppendByte('\\')
buf.AppendByte('n')
case '\r':
buf.AppendByte('\\')
buf.AppendByte('r')
case '\t':
buf.AppendByte('\\')
buf.AppendByte('t')
default:
// Encode bytes < 0x20, except for the escape sequences above.
buf.AppendString(`\u00`)
buf.AppendByte(hex[b>>4])
buf.AppendByte(hex[b&0xF])
}
i++
start = i
}
if start < len(s) {
buf.AppendString(s[start:])
}
}