github.com/kunnos/engine@v1.13.1/pkg/ioutils/multireader.go (about) 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 101 var offsetTo int64 102 103 for _, rdr := range r.readers { 104 size, err := getReadSeekerSize(rdr) 105 if err != nil { 106 return nil, -1, err 107 } 108 if offsetTo+size > offset { 109 return rdr, offset - offsetTo, nil 110 } 111 if rdr == r.readers[len(r.readers)-1] { 112 return rdr, offsetTo + offset, nil 113 } 114 offsetTo += size 115 } 116 117 return nil, 0, nil 118 } 119 120 func (r *multiReadSeeker) getCurOffset() (int64, error) { 121 var totalSize int64 122 for _, rdr := range r.readers[:r.pos.idx+1] { 123 if r.posIdx[rdr] == r.pos.idx { 124 totalSize += r.pos.offset 125 break 126 } 127 128 size, err := getReadSeekerSize(rdr) 129 if err != nil { 130 return -1, fmt.Errorf("error getting seeker size: %v", err) 131 } 132 totalSize += size 133 } 134 return totalSize, nil 135 } 136 137 func (r *multiReadSeeker) getOffsetToReader(rdr io.ReadSeeker) (int64, error) { 138 var offset int64 139 for _, r := range r.readers { 140 if r == rdr { 141 break 142 } 143 144 size, err := getReadSeekerSize(rdr) 145 if err != nil { 146 return -1, err 147 } 148 offset += size 149 } 150 return offset, nil 151 } 152 153 func (r *multiReadSeeker) Read(b []byte) (int, error) { 154 if r.pos == nil { 155 r.pos = &pos{0, 0} 156 } 157 158 bLen := int64(len(b)) 159 buf := bytes.NewBuffer(nil) 160 var rdr io.ReadSeeker 161 162 for _, rdr = range r.readers[r.pos.idx:] { 163 readBytes, err := io.CopyN(buf, rdr, bLen) 164 if err != nil && err != io.EOF { 165 return -1, err 166 } 167 bLen -= readBytes 168 169 if bLen == 0 { 170 break 171 } 172 } 173 174 rdrPos, err := rdr.Seek(0, os.SEEK_CUR) 175 if err != nil { 176 return -1, err 177 } 178 r.pos = &pos{r.posIdx[rdr], rdrPos} 179 return buf.Read(b) 180 } 181 182 func getReadSeekerSize(rdr io.ReadSeeker) (int64, error) { 183 // save the current position 184 pos, err := rdr.Seek(0, os.SEEK_CUR) 185 if err != nil { 186 return -1, err 187 } 188 189 // get the size 190 size, err := rdr.Seek(0, os.SEEK_END) 191 if err != nil { 192 return -1, err 193 } 194 195 // reset the position 196 if _, err := rdr.Seek(pos, os.SEEK_SET); err != nil { 197 return -1, err 198 } 199 return size, nil 200 } 201 202 // MultiReadSeeker returns a ReadSeeker that's the logical concatenation of the provided 203 // input readseekers. After calling this method the initial position is set to the 204 // beginning of the first ReadSeeker. At the end of a ReadSeeker, Read always advances 205 // to the beginning of the next ReadSeeker and returns EOF at the end of the last ReadSeeker. 206 // Seek can be used over the sum of lengths of all readseekers. 207 // 208 // When a MultiReadSeeker is used, no Read and Seek operations should be made on 209 // its ReadSeeker components. Also, users should make no assumption on the state 210 // of individual readseekers while the MultiReadSeeker is used. 211 func MultiReadSeeker(readers ...io.ReadSeeker) io.ReadSeeker { 212 if len(readers) == 1 { 213 return readers[0] 214 } 215 idx := make(map[io.ReadSeeker]int) 216 for i, rdr := range readers { 217 idx[rdr] = i 218 } 219 return &multiReadSeeker{ 220 readers: readers, 221 posIdx: idx, 222 } 223 }