1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
|
// Package ioutil implements some I/O utility functions.
package ioutil
import (
"bufio"
"context"
"errors"
"io"
ctxio "github.com/jbenet/go-context/io"
)
type readPeeker interface {
io.Reader
Peek(int) ([]byte, error)
}
var (
ErrEmptyReader = errors.New("reader is empty")
)
// NonEmptyReader takes a reader and returns it if it is not empty, or
// `ErrEmptyReader` if it is empty. If there is an error when reading the first
// byte of the given reader, it will be propagated.
func NonEmptyReader(r io.Reader) (io.Reader, error) {
pr, ok := r.(readPeeker)
if !ok {
pr = bufio.NewReader(r)
}
_, err := pr.Peek(1)
if err == io.EOF {
return nil, ErrEmptyReader
}
if err != nil {
return nil, err
}
return pr, nil
}
type readCloser struct {
io.Reader
closer io.Closer
}
func (r *readCloser) Close() error {
return r.closer.Close()
}
// NewReadCloser creates an `io.ReadCloser` with the given `io.Reader` and
// `io.Closer`.
func NewReadCloser(r io.Reader, c io.Closer) io.ReadCloser {
return &readCloser{Reader: r, closer: c}
}
type readCloserCloser struct {
io.ReadCloser
closer func() error
}
func (r *readCloserCloser) Close() (err error) {
defer func() {
if err == nil {
err = r.closer()
return
}
_ = r.closer()
}()
return r.ReadCloser.Close()
}
// NewReadCloserWithCloser creates an `io.ReadCloser` with the given `io.ReaderCloser` and
// `io.Closer` that ensures that the closer is closed on close
func NewReadCloserWithCloser(r io.ReadCloser, c func() error) io.ReadCloser {
return &readCloserCloser{ReadCloser: r, closer: c}
}
type writeCloser struct {
io.Writer
closer io.Closer
}
func (r *writeCloser) Close() error {
return r.closer.Close()
}
// NewWriteCloser creates an `io.WriteCloser` with the given `io.Writer` and
// `io.Closer`.
func NewWriteCloser(w io.Writer, c io.Closer) io.WriteCloser {
return &writeCloser{Writer: w, closer: c}
}
type writeNopCloser struct {
io.Writer
}
func (writeNopCloser) Close() error { return nil }
// WriteNopCloser returns a WriteCloser with a no-op Close method wrapping
// the provided Writer w.
func WriteNopCloser(w io.Writer) io.WriteCloser {
return writeNopCloser{w}
}
type readerAtAsReader struct {
io.ReaderAt
offset int64
}
func (r *readerAtAsReader) Read(bs []byte) (int, error) {
n, err := r.ReaderAt.ReadAt(bs, r.offset)
r.offset += int64(n)
return n, err
}
func NewReaderUsingReaderAt(r io.ReaderAt, offset int64) io.Reader {
return &readerAtAsReader{
ReaderAt: r,
offset: offset,
}
}
// CheckClose calls Close on the given io.Closer. If the given *error points to
// nil, it will be assigned the error returned by Close. Otherwise, any error
// returned by Close will be ignored. CheckClose is usually called with defer.
func CheckClose(c io.Closer, err *error) {
if cerr := c.Close(); cerr != nil && *err == nil {
*err = cerr
}
}
// NewContextWriter wraps a writer to make it respect given Context.
// If there is a blocking write, the returned Writer will return whenever the
// context is cancelled (the return values are n=0 and err=ctx.Err()).
func NewContextWriter(ctx context.Context, w io.Writer) io.Writer {
return ctxio.NewWriter(ctx, w)
}
// NewContextReader wraps a reader to make it respect given Context.
// If there is a blocking read, the returned Reader will return whenever the
// context is cancelled (the return values are n=0 and err=ctx.Err()).
func NewContextReader(ctx context.Context, r io.Reader) io.Reader {
return ctxio.NewReader(ctx, r)
}
// NewContextWriteCloser as NewContextWriter but with io.Closer interface.
func NewContextWriteCloser(ctx context.Context, w io.WriteCloser) io.WriteCloser {
ctxw := ctxio.NewWriter(ctx, w)
return NewWriteCloser(ctxw, w)
}
// NewContextReadCloser as NewContextReader but with io.Closer interface.
func NewContextReadCloser(ctx context.Context, r io.ReadCloser) io.ReadCloser {
ctxr := ctxio.NewReader(ctx, r)
return NewReadCloser(ctxr, r)
}
type readerOnError struct {
io.Reader
notify func(error)
}
// NewReaderOnError returns a io.Reader that call the notify function when an
// unexpected (!io.EOF) error happens, after call Read function.
func NewReaderOnError(r io.Reader, notify func(error)) io.Reader {
return &readerOnError{r, notify}
}
// NewReadCloserOnError returns a io.ReadCloser that call the notify function
// when an unexpected (!io.EOF) error happens, after call Read function.
func NewReadCloserOnError(r io.ReadCloser, notify func(error)) io.ReadCloser {
return NewReadCloser(NewReaderOnError(r, notify), r)
}
func (r *readerOnError) Read(buf []byte) (n int, err error) {
n, err = r.Reader.Read(buf)
if err != nil && err != io.EOF {
r.notify(err)
}
return
}
type writerOnError struct {
io.Writer
notify func(error)
}
// NewWriterOnError returns a io.Writer that call the notify function when an
// unexpected (!io.EOF) error happens, after call Write function.
func NewWriterOnError(w io.Writer, notify func(error)) io.Writer {
return &writerOnError{w, notify}
}
// NewWriteCloserOnError returns a io.WriteCloser that call the notify function
// when an unexpected (!io.EOF) error happens, after call Write function.
func NewWriteCloserOnError(w io.WriteCloser, notify func(error)) io.WriteCloser {
return NewWriteCloser(NewWriterOnError(w, notify), w)
}
func (r *writerOnError) Write(p []byte) (n int, err error) {
n, err = r.Writer.Write(p)
if err != nil && err != io.EOF {
r.notify(err)
}
return
}
|