-
Notifications
You must be signed in to change notification settings - Fork 19
/
csv.go
84 lines (75 loc) · 1.45 KB
/
csv.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
package main
import "io"
type state int
const (
// most common state. Outside of quoted field.
start state = iota
// in quoted field
quoted
// in quoted field and that previous character was a backslash
escape
)
type converter struct {
delegate io.Reader
buf []byte // place we read into
remaining []byte // what is still left to be read from
escaped []byte // if non-empty, contains raw bytes ready to be copied to output, before remaining
s state
}
func newConverter(r io.Reader) *converter {
return &converter{
delegate: r,
buf: make([]byte, 4092),
}
}
func (c *converter) Read(p []byte) (n int, err error) {
if len(c.escaped) != 0 {
n = copy(p, c.escaped)
c.escaped = c.escaped[n:]
return n, nil
}
if len(c.remaining) == 0 {
n, err = c.delegate.Read(c.buf)
if n == 0 {
return n, err
}
c.remaining = c.buf[:n]
}
i := 0 // cursor to p
for i < len(p) && len(c.remaining) != 0 {
next := c.remaining[0]
c.remaining = c.remaining[1:]
switch c.s {
case start:
p[i] = next
i++
if next == '"' {
c.s = quoted
}
case quoted:
switch next {
case '"':
p[i] = next
i++
c.s = start
case '\\':
c.s = escape
default:
p[i] = next
i++
}
case escape:
switch next {
case '"':
c.escaped = []byte{'"', '"'}
case 'n':
c.escaped = []byte{'\n'}
default:
c.escaped = []byte{next}
}
c.s = quoted
return i, err
}
}
return i, err
}