4
4
5
5
package mime
6
6
7
- import (
8
- "strings"
7
+ const tspecialsString = `()<>@,;:\"/[]?=`
8
+
9
+ var (
10
+ tspecials asciiSet
11
+ tokenChars asciiSet
9
12
)
10
13
11
- // isTSpecial reports whether rune is in 'tspecials' as defined by RFC
14
+ func init () {
15
+ // tspecials := "(" / ")" / "<" / ">" / "@" /
16
+ // "," / ";" / ":" / "\" / <">
17
+ // "/" / "[" / "]" / "?" / "="
18
+ tspecials .add (tspecialsString )
19
+
20
+ // token := 1*<any (US-ASCII) CHAR except SPACE, CTLs,
21
+ // or tspecials>
22
+ tokenChars .addRange ('!' , 0x7f )
23
+ tokenChars .remove (tspecialsString )
24
+ }
25
+
26
+ // isTSpecial reports whether c is in 'tspecials' as defined by RFC
12
27
// 1521 and RFC 2045.
13
- func isTSpecial (r rune ) bool {
14
- return strings . ContainsRune ( `()<>@,;:\"/[]?=` , r )
28
+ func isTSpecial (c byte ) bool {
29
+ return tspecials . contains ( c )
15
30
}
16
31
17
- // isTokenChar reports whether rune is in 'token' as defined by RFC
32
+ // isTokenChar reports whether c is in 'token' as defined by RFC
18
33
// 1521 and RFC 2045.
19
- func isTokenChar (r rune ) bool {
20
- // token := 1*<any (US-ASCII) CHAR except SPACE, CTLs,
21
- // or tspecials>
22
- return r > 0x20 && r < 0x7f && ! isTSpecial (r )
34
+ func isTokenChar (c byte ) bool {
35
+ return tokenChars .contains (c )
23
36
}
24
37
25
38
// isToken reports whether s is a 'token' as defined by RFC 1521
@@ -28,5 +41,48 @@ func isToken(s string) bool {
28
41
if s == "" {
29
42
return false
30
43
}
31
- return strings .IndexFunc (s , isNotTokenChar ) < 0
44
+ for _ , c := range []byte (s ) {
45
+ if ! tokenChars .contains (c ) {
46
+ return false
47
+ }
48
+ }
49
+ return true
50
+ }
51
+
52
+ // asciiSet is a 32-byte value, where each bit represents the presence of a
53
+ // given ASCII character in the set. The 128-bits of the lower 16 bytes,
54
+ // starting with the least-significant bit of the lowest word to the
55
+ // most-significant bit of the highest word, map to the full range of all
56
+ // 128 ASCII characters. The 128-bits of the upper 16 bytes will be zeroed,
57
+ // ensuring that any non-ASCII character will be reported as not in the set.
58
+ // This allocates a total of 32 bytes even though the upper half
59
+ // is unused to avoid bounds checks in asciiSet.contains.
60
+ type asciiSet [8 ]uint32
61
+
62
+ // add adds all the characters in chars to the set.
63
+ // Precondition: all the characters in chars are ASCII.
64
+ func (as * asciiSet ) add (chars string ) {
65
+ for _ , c := range []byte (chars ) {
66
+ as [c / 32 ] |= 1 << (c % 32 )
67
+ }
68
+ }
69
+
70
+ // addRange adds all the characters between lo (inclusive) and hi (exclusive) to the set.
71
+ // Precondition: hi <= utf8.RuneSelf (0x80)
72
+ func (as * asciiSet ) addRange (lo , hi byte ) {
73
+ for c := lo ; c < hi ; c ++ {
74
+ as [c / 32 ] |= 1 << (c % 32 )
75
+ }
76
+ }
77
+
78
+ // remove removes all the characters in chars from the set.
79
+ func (as * asciiSet ) remove (chars string ) {
80
+ for _ , c := range []byte (chars ) {
81
+ as [c / 32 ] &^= 1 << (c % 32 )
82
+ }
83
+ }
84
+
85
+ // contains reports whether c is inside the set.
86
+ func (as * asciiSet ) contains (c byte ) bool {
87
+ return (as [c / 32 ] & (1 << (c % 32 ))) != 0
32
88
}
0 commit comments