]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob
0d2d76b47979f0ec2445e6926cf5ac0fd67e8fbb
[github/fretlink/terraform-provider-statuscake.git] /
1 package ioutils
2
3 import (
4 "bytes"
5 "fmt"
6 "io"
7 "os"
8 )
9
10 type pos struct {
11 idx int
12 offset int64
13 }
14
15 type multiReadSeeker struct {
16 readers []io.ReadSeeker
17 pos *pos
18 posIdx map[io.ReadSeeker]int
19 }
20
21 func (r *multiReadSeeker) Seek(offset int64, whence int) (int64, error) {
22 var tmpOffset int64
23 switch whence {
24 case os.SEEK_SET:
25 for i, rdr := range r.readers {
26 // get size of the current reader
27 s, err := rdr.Seek(0, os.SEEK_END)
28 if err != nil {
29 return -1, err
30 }
31
32 if offset > tmpOffset+s {
33 if i == len(r.readers)-1 {
34 rdrOffset := s + (offset - tmpOffset)
35 if _, err := rdr.Seek(rdrOffset, os.SEEK_SET); err != nil {
36 return -1, err
37 }
38 r.pos = &pos{i, rdrOffset}
39 return offset, nil
40 }
41
42 tmpOffset += s
43 continue
44 }
45
46 rdrOffset := offset - tmpOffset
47 idx := i
48
49 rdr.Seek(rdrOffset, os.SEEK_SET)
50 // make sure all following readers are at 0
51 for _, rdr := range r.readers[i+1:] {
52 rdr.Seek(0, os.SEEK_SET)
53 }
54
55 if rdrOffset == s && i != len(r.readers)-1 {
56 idx++
57 rdrOffset = 0
58 }
59 r.pos = &pos{idx, rdrOffset}
60 return offset, nil
61 }
62 case os.SEEK_END:
63 for _, rdr := range r.readers {
64 s, err := rdr.Seek(0, os.SEEK_END)
65 if err != nil {
66 return -1, err
67 }
68 tmpOffset += s
69 }
70 r.Seek(tmpOffset+offset, os.SEEK_SET)
71 return tmpOffset + offset, nil
72 case os.SEEK_CUR:
73 if r.pos == nil {
74 return r.Seek(offset, os.SEEK_SET)
75 }
76 // Just return the current offset
77 if offset == 0 {
78 return r.getCurOffset()
79 }
80
81 curOffset, err := r.getCurOffset()
82 if err != nil {
83 return -1, err
84 }
85 rdr, rdrOffset, err := r.getReaderForOffset(curOffset + offset)
86 if err != nil {
87 return -1, err
88 }
89
90 r.pos = &pos{r.posIdx[rdr], rdrOffset}
91 return curOffset + offset, nil
92 default:
93 return -1, fmt.Errorf("Invalid whence: %d", whence)
94 }
95
96 return -1, fmt.Errorf("Error seeking for whence: %d, offset: %d", whence, offset)
97 }
98
99 func (r *multiReadSeeker) getReaderForOffset(offset int64) (io.ReadSeeker, int64, error) {
100 var rdr io.ReadSeeker
101 var rdrOffset int64
102
103 for i, rdr := range r.readers {
104 offsetTo, err := r.getOffsetToReader(rdr)
105 if err != nil {
106 return nil, -1, err
107 }
108 if offsetTo > offset {
109 rdr = r.readers[i-1]
110 rdrOffset = offsetTo - offset
111 break
112 }
113
114 if rdr == r.readers[len(r.readers)-1] {
115 rdrOffset = offsetTo + offset
116 break
117 }
118 }
119
120 return rdr, rdrOffset, nil
121 }
122
123 func (r *multiReadSeeker) getCurOffset() (int64, error) {
124 var totalSize int64
125 for _, rdr := range r.readers[:r.pos.idx+1] {
126 if r.posIdx[rdr] == r.pos.idx {
127 totalSize += r.pos.offset
128 break
129 }
130
131 size, err := getReadSeekerSize(rdr)
132 if err != nil {
133 return -1, fmt.Errorf("error getting seeker size: %v", err)
134 }
135 totalSize += size
136 }
137 return totalSize, nil
138 }
139
140 func (r *multiReadSeeker) getOffsetToReader(rdr io.ReadSeeker) (int64, error) {
141 var offset int64
142 for _, r := range r.readers {
143 if r == rdr {
144 break
145 }
146
147 size, err := getReadSeekerSize(rdr)
148 if err != nil {
149 return -1, err
150 }
151 offset += size
152 }
153 return offset, nil
154 }
155
156 func (r *multiReadSeeker) Read(b []byte) (int, error) {
157 if r.pos == nil {
158 r.pos = &pos{0, 0}
159 }
160
161 bCap := int64(cap(b))
162 buf := bytes.NewBuffer(nil)
163 var rdr io.ReadSeeker
164
165 for _, rdr = range r.readers[r.pos.idx:] {
166 readBytes, err := io.CopyN(buf, rdr, bCap)
167 if err != nil && err != io.EOF {
168 return -1, err
169 }
170 bCap -= readBytes
171
172 if bCap == 0 {
173 break
174 }
175 }
176
177 rdrPos, err := rdr.Seek(0, os.SEEK_CUR)
178 if err != nil {
179 return -1, err
180 }
181 r.pos = &pos{r.posIdx[rdr], rdrPos}
182 return buf.Read(b)
183 }
184
185 func getReadSeekerSize(rdr io.ReadSeeker) (int64, error) {
186 // save the current position
187 pos, err := rdr.Seek(0, os.SEEK_CUR)
188 if err != nil {
189 return -1, err
190 }
191
192 // get the size
193 size, err := rdr.Seek(0, os.SEEK_END)
194 if err != nil {
195 return -1, err
196 }
197
198 // reset the position
199 if _, err := rdr.Seek(pos, os.SEEK_SET); err != nil {
200 return -1, err
201 }
202 return size, nil
203 }
204
205 // MultiReadSeeker returns a ReadSeeker that's the logical concatenation of the provided
206 // input readseekers. After calling this method the initial position is set to the
207 // beginning of the first ReadSeeker. At the end of a ReadSeeker, Read always advances
208 // to the beginning of the next ReadSeeker and returns EOF at the end of the last ReadSeeker.
209 // Seek can be used over the sum of lengths of all readseekers.
210 //
211 // When a MultiReadSeeker is used, no Read and Seek operations should be made on
212 // its ReadSeeker components. Also, users should make no assumption on the state
213 // of individual readseekers while the MultiReadSeeker is used.
214 func MultiReadSeeker(readers ...io.ReadSeeker) io.ReadSeeker {
215 if len(readers) == 1 {
216 return readers[0]
217 }
218 idx := make(map[io.ReadSeeker]int)
219 for i, rdr := range readers {
220 idx[rdr] = i
221 }
222 return &multiReadSeeker{
223 readers: readers,
224 posIdx: idx,
225 }
226 }