-
-
Notifications
You must be signed in to change notification settings - Fork 9
Expand file tree
/
Copy pathdisplaystring.go
More file actions
105 lines (85 loc) · 2.03 KB
/
displaystring.go
File metadata and controls
105 lines (85 loc) · 2.03 KB
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
package httpsfv
import (
"encoding/hex"
"errors"
"strings"
"unicode"
"unicode/utf8"
)
type DisplayString string
var ErrInvalidDisplayString = errors.New("invalid display string type")
var notVcharOrSp = &unicode.RangeTable{
R16: []unicode.Range16{
{0x0000, 0x001f, 1},
{0x007f, 0x00ff, 1},
},
LatinOffset: 2,
}
// marshalSFV serializes as defined in
// https://httpwg.org/specs/rfc9651.html#ser-string.
func (s DisplayString) marshalSFV(b *strings.Builder) error {
if _, err := b.WriteString(`%"`); err != nil {
return err
}
for i := 0; i < len(s); i++ {
if s[i] == '%' || s[i] == '"' || unicode.Is(notVcharOrSp, rune(s[i])) {
b.WriteRune('%')
b.WriteString(hex.EncodeToString([]byte{s[i]}))
continue
}
b.WriteByte(s[i])
}
b.WriteByte('"')
return nil
}
// parseDisplayString parses as defined in
// https://httpwg.org/specs/rfc9651.html#parse-display.
func parseDisplayString(s *scanner) (DisplayString, error) {
if s.eof() || len(s.data[s.off:]) < 2 || s.data[s.off:2] != `%"` {
return "", &UnmarshalError{s.off, ErrInvalidDisplayString}
}
s.off += 2
var b strings.Builder
for !s.eof() {
c := s.data[s.off]
s.off++
switch c {
case '%':
if len(s.data[s.off:]) < 2 {
return "", &UnmarshalError{s.off, ErrInvalidDisplayString}
}
c0 := unhex(s.data[s.off])
if c0 == 0 {
return "", &UnmarshalError{s.off, ErrInvalidDisplayString}
}
c1 := unhex(s.data[s.off+1])
if c1 == 0 {
return "", &UnmarshalError{s.off, ErrInvalidDisplayString}
}
b.WriteByte(c0<<4 | c1)
s.off += 2
case '"':
r := b.String()
if !utf8.ValidString(r) {
return "", ErrInvalidDisplayString
}
return DisplayString(r), nil
default:
if unicode.Is(notVcharOrSp, rune(c)) {
return "", &UnmarshalError{s.off, ErrInvalidDisplayString}
}
b.WriteByte(c)
}
}
return "", &UnmarshalError{s.off, ErrInvalidDisplayString}
}
func unhex(c byte) byte {
switch {
case '0' <= c && c <= '9':
return c - '0'
case 'a' <= c && c <= 'f':
return c - 'a' + 10
default:
return 0
}
}