storj/internal/readcloser/multi.go

72 lines
1.7 KiB
Go

// Copyright (C) 2018 Storj Labs, Inc.
// See LICENSE for copying information.
package readcloser
import (
"io"
"storj.io/storj/pkg/utils"
)
type eofReadCloser struct{}
func (eofReadCloser) Read([]byte) (int, error) {
return 0, io.EOF
}
func (eofReadCloser) Close() error {
return nil
}
type multiReadCloser struct {
readers []io.ReadCloser
}
// MultiReadCloser is a MultiReader extension that returns a ReaderCloser
// that's the logical concatenation of the provided input readers.
// They're read sequentially. Once all inputs have returned EOF,
// Read will return EOF. If any of the readers return a non-nil,
// non-EOF error, Read will return that error.
func MultiReadCloser(readers ...io.ReadCloser) io.ReadCloser {
r := make([]io.ReadCloser, len(readers))
copy(r, readers)
return &multiReadCloser{r}
}
func (mr *multiReadCloser) Read(p []byte) (n int, err error) {
for len(mr.readers) > 0 {
// Optimization to flatten nested multiReaders.
if len(mr.readers) == 1 {
if r, ok := mr.readers[0].(*multiReadCloser); ok {
mr.readers = r.readers
continue
}
}
n, err = mr.readers[0].Read(p)
if err == io.EOF {
utils.LogClose(mr.readers[0])
// Use eofReader instead of nil to avoid nil panic
// after performing flatten (Issue 18232).
mr.readers[0] = eofReadCloser{} // permit earlier GC
mr.readers = mr.readers[1:]
}
if n > 0 || err != io.EOF {
if err == io.EOF && len(mr.readers) > 0 {
// Don't return EOF yet. More readers remain.
err = nil
}
return
}
}
return 0, io.EOF
}
func (mr *multiReadCloser) Close() error {
errs := make([]error, len(mr.readers))
for i, r := range mr.readers {
errs[i] = r.Close()
}
return utils.CombineErrors(errs...)
}