-
Notifications
You must be signed in to change notification settings - Fork 0
/
readers.go
169 lines (149 loc) · 3.6 KB
/
readers.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
163
164
165
166
167
168
169
package pngutil
import (
"errors"
"io"
)
/*
skipReadSeeker represents a view into a larger reader. It starts at
offset and ends at limit. Once it has read up to limit the Read
method returns an io.EOF error.
In this package all instances of skipReadSeeker use the same underlying
reader passed to ReplaceMeta.
*/
type skipReadSeeker struct {
name string
rs io.ReadSeeker
start int64
end int64
/*
offset is relative to start. That is,
it always begins at zero even if start
is not.
*/
offset int64
}
func (srs *skipReadSeeker) Read(p []byte) (n int, err error) {
if srs.offset >= srs.end {
return 0, io.EOF
}
toRead := srs.end - srs.offset
if toRead > int64(len(p)) {
toRead = int64(len(p))
}
n, err = srs.rs.Read(p[:toRead])
srs.offset += int64(n)
return n, err
}
func (srs *skipReadSeeker) Seek(offset int64, whence int) (n int64, err error) {
switch whence {
case io.SeekStart:
offset += srs.start
case io.SeekCurrent:
offset += srs.start + srs.offset
case io.SeekEnd:
offset = srs.end + offset
}
if offset < srs.start {
return n, errors.New("pngutil: skipReadSeeker seeking before start")
}
n, err = srs.rs.Seek(offset, io.SeekStart)
srs.offset = offset
return offset, err
}
type multiReadSeeker struct {
overall int64
rsIdx int
readSeekers []*skipReadSeeker
sizes []int64
size int64
}
/*
Returns a new multireader that is a concatenation of
rs. All read seekers and the multireader itself will
be seeked to the start.
*/
func newMultiReadSeeker(readSeekers ...*skipReadSeeker) (mrs *multiReadSeeker, err error) {
var sizes []int64
var size int64
for _, rs := range readSeekers {
sz := rs.end - rs.start
sizes = append(sizes, sz)
size += sz
}
mrs = &multiReadSeeker{
readSeekers: readSeekers,
sizes: sizes,
size: size,
}
/*
ReplaceMeta uses its input read seeker to create
two or more of the read seekers supplied to this
function. Therefore we seek to the start here to
ensure all readers are in the correct position.
*/
if _, err := mrs.Seek(0, io.SeekStart); err != nil {
return nil, err
}
return mrs, nil
}
func (mrs *multiReadSeeker) Read(p []byte) (n int, err error) {
read := 0
for {
if read == len(p) {
break
}
n, err = mrs.readSeekers[mrs.rsIdx].Read(p[read:])
read += n
mrs.overall += int64(n)
// If we reach the end of the current readseeker...
if errors.Is(err, io.EOF) {
// ...return if this is the last readseeker.
if mrs.rsIdx == len(mrs.readSeekers)-1 {
return read, err
}
/*
Otherwise increment readseeker index, ensure
said readseekers's cursor is at the start,
then resume reading.
*/
mrs.rsIdx++
_, _ = mrs.readSeekers[mrs.rsIdx].Seek(0, io.SeekStart)
continue
}
// Immediately return on non-EOF error.
if err != nil {
return read, err
}
}
return read, nil
}
func (mrs *multiReadSeeker) Seek(offset int64, whence int) (n int64, err error) {
switch whence {
case io.SeekStart:
// to prevent default case
case io.SeekCurrent:
offset += mrs.overall
case io.SeekEnd:
offset = mrs.size + offset
default:
return 0, errors.New("pngutil: invalid whence value for multiReadSeeker")
}
var total int64
for i, s := range mrs.sizes {
if offset >= total && offset < total+s {
mrs.rsIdx = i
rsOffset := offset - total
_, err := mrs.readSeekers[mrs.rsIdx].Seek(rsOffset, io.SeekStart)
if err != nil {
return 0, err
}
mrs.overall = offset
return offset, nil
}
total += s
}
return 0, errors.New("pngutil: seek out of bounds for multiReadSeeker")
}
func (mrs *multiReadSeeker) Size() (n int64) {
return mrs.size
}