1package playlistfs 2 3import ( 4 "errors" 5 "io" 6 "strings" 7) 8 9type RecvInput interface {10 CurrentReader() *strings.Reader11 NextReader() *strings.Reader12}1314// BlockRecv implements io.Reader on top of RecvInput. When the15// implemented Read() returns io.EOF and the caller calls Read()16// again the reader implemented by BlockRecv will block until17// new data is available.18type BlockRecv struct {19 // Absolute offset at which (and beyond which) EOF will be returned.20 // This member must be initalized with -1.21 eofAt int642223 // Absolute base offset used to calculate a relative offset for24 // the current string reader.25 baseOff int642627 // Current string reader on which the read function operates.28 reader *strings.Reader2930 in RecvInput31}3233func NewBlockRecv(in RecvInput) *BlockRecv {34 return &BlockRecv{eofAt: -1, in: in}35}3637func (e *BlockRecv) Read(off int64, p []byte) (int, error) {38 if e.reader == nil {39 e.reader = e.in.CurrentReader()40 } else if e.eofAt > 0 && off >= e.eofAt {41 // We are reading beyond EOF for the second time.42 // Block until new data is available and return it.43 e.baseOff += e.reader.Size()44 e.reader = e.in.NextReader()45 } else if off < e.baseOff {46 return 0, errors.New("invalid seek")47 }4849 // Calculate offset relative to current reader50 relOff := off - e.baseOff5152 _, err := e.reader.Seek(relOff, io.SeekStart)53 if err != nil {54 e.eofAt = off55 return 0, io.EOF56 }5758 n, err := e.reader.Read(p)59 if err == io.EOF || (err != nil && n == 0) {60 e.eofAt = off + int64(n)61 return 0, io.EOF62 }6364 return n, nil65}