github.com/bhojpur/cache@v0.0.4/pkg/ioutils/readers.go (about)

     1  package ioutils
     2  
     3  // Copyright (c) 2018 Bhojpur Consulting Private Limited, India. All rights reserved.
     4  
     5  // Permission is hereby granted, free of charge, to any person obtaining a copy
     6  // of this software and associated documentation files (the "Software"), to deal
     7  // in the Software without restriction, including without limitation the rights
     8  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     9  // copies of the Software, and to permit persons to whom the Software is
    10  // furnished to do so, subject to the following conditions:
    11  
    12  // The above copyright notice and this permission notice shall be included in
    13  // all copies or substantial portions of the Software.
    14  
    15  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    16  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    17  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    18  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    19  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    20  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    21  // THE SOFTWARE.
    22  
    23  import (
    24  	"context"
    25  	"io"
    26  
    27  	// make sure crypto.SHA256, crypto.sha512 and crypto.SHA384 are registered
    28  	_ "crypto/sha256"
    29  	_ "crypto/sha512"
    30  )
    31  
    32  // ReadCloserWrapper wraps an io.Reader, and implements an io.ReadCloser
    33  // It calls the given callback function when closed. It should be constructed
    34  // with NewReadCloserWrapper
    35  type ReadCloserWrapper struct {
    36  	io.Reader
    37  	closer func() error
    38  }
    39  
    40  // Close calls back the passed closer function
    41  func (r *ReadCloserWrapper) Close() error {
    42  	return r.closer()
    43  }
    44  
    45  // NewReadCloserWrapper returns a new io.ReadCloser.
    46  func NewReadCloserWrapper(r io.Reader, closer func() error) io.ReadCloser {
    47  	return &ReadCloserWrapper{
    48  		Reader: r,
    49  		closer: closer,
    50  	}
    51  }
    52  
    53  type readerErrWrapper struct {
    54  	reader io.Reader
    55  	closer func()
    56  }
    57  
    58  func (r *readerErrWrapper) Read(p []byte) (int, error) {
    59  	n, err := r.reader.Read(p)
    60  	if err != nil {
    61  		r.closer()
    62  	}
    63  	return n, err
    64  }
    65  
    66  // NewReaderErrWrapper returns a new io.Reader.
    67  func NewReaderErrWrapper(r io.Reader, closer func()) io.Reader {
    68  	return &readerErrWrapper{
    69  		reader: r,
    70  		closer: closer,
    71  	}
    72  }
    73  
    74  // OnEOFReader wraps an io.ReadCloser and a function
    75  // the function will run at the end of file or close the file.
    76  type OnEOFReader struct {
    77  	Rc io.ReadCloser
    78  	Fn func()
    79  }
    80  
    81  func (r *OnEOFReader) Read(p []byte) (n int, err error) {
    82  	n, err = r.Rc.Read(p)
    83  	if err == io.EOF {
    84  		r.runFunc()
    85  	}
    86  	return
    87  }
    88  
    89  // Close closes the file and run the function.
    90  func (r *OnEOFReader) Close() error {
    91  	err := r.Rc.Close()
    92  	r.runFunc()
    93  	return err
    94  }
    95  
    96  func (r *OnEOFReader) runFunc() {
    97  	if fn := r.Fn; fn != nil {
    98  		fn()
    99  		r.Fn = nil
   100  	}
   101  }
   102  
   103  // cancelReadCloser wraps an io.ReadCloser with a context for cancelling read
   104  // operations.
   105  type cancelReadCloser struct {
   106  	cancel func()
   107  	pR     *io.PipeReader // Stream to read from
   108  	pW     *io.PipeWriter
   109  }
   110  
   111  // NewCancelReadCloser creates a wrapper that closes the ReadCloser when the
   112  // context is cancelled. The returned io.ReadCloser must be closed when it is
   113  // no longer needed.
   114  func NewCancelReadCloser(ctx context.Context, in io.ReadCloser) io.ReadCloser {
   115  	pR, pW := io.Pipe()
   116  
   117  	// Create a context used to signal when the pipe is closed
   118  	doneCtx, cancel := context.WithCancel(context.Background())
   119  
   120  	p := &cancelReadCloser{
   121  		cancel: cancel,
   122  		pR:     pR,
   123  		pW:     pW,
   124  	}
   125  
   126  	go func() {
   127  		_, err := io.Copy(pW, in)
   128  		select {
   129  		case <-ctx.Done():
   130  			// If the context was closed, p.closeWithError
   131  			// was already called. Calling it again would
   132  			// change the error that Read returns.
   133  		default:
   134  			p.closeWithError(err)
   135  		}
   136  		in.Close()
   137  	}()
   138  	go func() {
   139  		for {
   140  			select {
   141  			case <-ctx.Done():
   142  				p.closeWithError(ctx.Err())
   143  			case <-doneCtx.Done():
   144  				return
   145  			}
   146  		}
   147  	}()
   148  
   149  	return p
   150  }
   151  
   152  // Read wraps the Read method of the pipe that provides data from the wrapped
   153  // ReadCloser.
   154  func (p *cancelReadCloser) Read(buf []byte) (n int, err error) {
   155  	return p.pR.Read(buf)
   156  }
   157  
   158  // closeWithError closes the wrapper and its underlying reader. It will
   159  // cause future calls to Read to return err.
   160  func (p *cancelReadCloser) closeWithError(err error) {
   161  	p.pW.CloseWithError(err)
   162  	p.cancel()
   163  }
   164  
   165  // Close closes the wrapper its underlying reader. It will cause
   166  // future calls to Read to return io.EOF.
   167  func (p *cancelReadCloser) Close() error {
   168  	p.closeWithError(io.EOF)
   169  	return nil
   170  }