-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdecoder.go
162 lines (136 loc) · 3.6 KB
/
decoder.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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
package urldecode
import (
"encoding/hex"
"errors"
"io"
)
const bufferSize = 1024
// Decoder holds the decoder's internal state
type Decoder struct {
source io.Reader
buffer []byte
offset int
}
// NewDecoder returns a new decoder for the given source reader
func NewDecoder(r io.Reader) *Decoder {
return &Decoder{
source: r,
buffer: make([]byte, bufferSize)[:0],
}
}
// NextPair returns the next key/value pair in the stream or EOF if there are no more pairs
func (d *Decoder) NextPair() (string, io.Reader, error) {
err := d.fill()
if len(d.buffer) == 0 {
return "", nil, err
}
for d.offset < len(d.buffer) {
input := d.buffer[d.offset]
if input == '&' {
return string(d.buffer[:d.offset]), d.newValueReader(), nil
} else if input == '=' {
d.offset++
return string(d.buffer[:d.offset-1]), d.newValueReader(), nil
} else {
d.offset++
}
}
if d.offset < cap(d.buffer) {
return string(d.buffer), d.newValueReader(), nil
}
return "", nil, errors.New("key is too long")
}
// Read decodes the value of a key/value pair
func (r *valueReader) Read(p []byte) (offset int, err error) {
for offset < len(p) {
// flush the output buffer if required
if (r.flush || r.eof) && len(r.outputBuffer) > 0 {
p[offset] = r.outputBuffer[0]
offset++
r.outputBuffer = r.outputBuffer[1:]
continue
}
// try to read more data from the source if necessary
if !r.eof && r.decoder.offset == len(r.decoder.buffer) {
r.decoder.fill()
r.eof = len(r.decoder.buffer) == 0
continue // give us a chance to flush output buffer before returning eof
}
if r.eof {
return offset, io.EOF
}
// we are not at EOF and ready to read the next byte
input := r.decoder.buffer[r.decoder.offset]
r.decoder.offset++
if input == '&' {
r.eof = true
} else {
offset = r.step(r, input, p, offset)
}
}
return offset, nil
}
func (d *Decoder) newValueReader() *valueReader {
return &valueReader{decoder: d, step: defaultStep}
}
func (d *Decoder) fill() error {
if d.offset > 0 {
n := copy(d.buffer, d.buffer[d.offset:])
d.buffer = d.buffer[:n]
d.offset = 0
}
// handle error later since we can have n > 0 and err != nil
n, err := d.source.Read(d.buffer[len(d.buffer):cap(d.buffer)])
d.buffer = d.buffer[0 : len(d.buffer)+n]
return err
}
type valueReader struct {
decoder *Decoder
eof bool
memory [3]byte
outputBuffer []byte
step scanStep
flush bool
}
type scanStep func(*valueReader, byte, []byte, int) int
// defaultStep is the normal state where we copy things verbatim (and handle +)
func defaultStep(r *valueReader, input byte, p []byte, offset int) int {
if input == '%' {
r.step = escape
r.flush = false
r.memory[0] = '%'
r.outputBuffer = r.memory[:1]
return offset
}
if input == '+' {
p[offset] = ' '
} else {
p[offset] = input
}
return offset + 1
}
// escape is the state after reading %
func escape(r *valueReader, input byte, p []byte, offset int) int {
r.memory[1] = input
r.outputBuffer = r.memory[:2]
if input >= '0' && input <= '9' || input >= 'a' && input <= 'f' || input >= 'A' && input <= 'F' {
r.step = escape1
} else {
r.step = defaultStep
r.flush = true
}
return offset
}
// escape is the state after reading %x where x is a hex digit
func escape1(r *valueReader, input byte, p []byte, offset int) int {
r.memory[2] = input
r.outputBuffer = r.memory[:3]
r.step = defaultStep
r.flush = true
if input >= '0' && input <= '9' || input >= 'a' && input <= 'f' || input >= 'A' && input <= 'F' {
hex.Decode(p[offset:offset+1], r.outputBuffer[1:])
r.outputBuffer = nil
return offset + 1
}
return offset
}