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  }