github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/talks/2013/oscon-dl/sizereaderat.go (about) 1 // +build ignore,OMIT 2 3 package sizereaderat 4 5 import ( 6 "io" 7 "sort" 8 ) 9 10 // START_1 OMIT 11 // A SizeReaderAt is a ReaderAt with a Size method. 12 // 13 // An io.SectionReader implements SizeReaderAt. 14 type SizeReaderAt interface { 15 Size() int64 16 io.ReaderAt 17 } 18 19 // NewMultiReaderAt is like io.MultiReader but produces a ReaderAt 20 // (and Size), instead of just a reader. 21 func NewMultiReaderAt(parts ...SizeReaderAt) SizeReaderAt { 22 m := &multi{ 23 parts: make([]offsetAndSource, 0, len(parts)), 24 } 25 var off int64 26 for _, p := range parts { 27 m.parts = append(m.parts, offsetAndSource{off, p}) 28 off += p.Size() 29 } 30 m.size = off 31 return m 32 } 33 34 // END_1 OMIT 35 36 type offsetAndSource struct { 37 off int64 38 SizeReaderAt 39 } 40 41 type multi struct { 42 parts []offsetAndSource 43 size int64 44 } 45 46 func (m *multi) Size() int64 { return m.size } 47 48 func (m *multi) ReadAt(p []byte, off int64) (n int, err error) { 49 wantN := len(p) 50 51 // Skip past the requested offset. 52 skipParts := sort.Search(len(m.parts), func(i int) bool { 53 // This function returns whether parts[i] will 54 // contribute any bytes to our output. 55 part := m.parts[i] 56 return part.off+part.Size() > off 57 }) 58 parts := m.parts[skipParts:] 59 60 // How far to skip in the first part. 61 needSkip := off 62 if len(parts) > 0 { 63 needSkip -= parts[0].off 64 } 65 66 for len(parts) > 0 && len(p) > 0 { 67 readP := p 68 partSize := parts[0].Size() 69 if int64(len(readP)) > partSize-needSkip { 70 readP = readP[:partSize-needSkip] 71 } 72 pn, err0 := parts[0].ReadAt(readP, needSkip) 73 if err0 != nil { 74 return n, err0 75 } 76 n += pn 77 p = p[pn:] 78 if int64(pn)+needSkip == partSize { 79 parts = parts[1:] 80 } 81 needSkip = 0 82 } 83 84 if n != wantN { 85 err = io.ErrUnexpectedEOF 86 } 87 return 88 }