github.com/xmplusdev/xmcore@v1.8.11-0.20240412132628-5518b55526af/common/buf/copy.go (about)

     1  package buf
     2  
     3  import (
     4  	"io"
     5  	"time"
     6  
     7  	"github.com/xmplusdev/xmcore/common/errors"
     8  	"github.com/xmplusdev/xmcore/common/signal"
     9  	"github.com/xmplusdev/xmcore/features/stats"
    10  )
    11  
    12  type dataHandler func(MultiBuffer)
    13  
    14  type copyHandler struct {
    15  	onData []dataHandler
    16  }
    17  
    18  // SizeCounter is for counting bytes copied by Copy().
    19  type SizeCounter struct {
    20  	Size int64
    21  }
    22  
    23  // CopyOption is an option for copying data.
    24  type CopyOption func(*copyHandler)
    25  
    26  // UpdateActivity is a CopyOption to update activity on each data copy operation.
    27  func UpdateActivity(timer signal.ActivityUpdater) CopyOption {
    28  	return func(handler *copyHandler) {
    29  		handler.onData = append(handler.onData, func(MultiBuffer) {
    30  			timer.Update()
    31  		})
    32  	}
    33  }
    34  
    35  // CountSize is a CopyOption that sums the total size of data copied into the given SizeCounter.
    36  func CountSize(sc *SizeCounter) CopyOption {
    37  	return func(handler *copyHandler) {
    38  		handler.onData = append(handler.onData, func(b MultiBuffer) {
    39  			sc.Size += int64(b.Len())
    40  		})
    41  	}
    42  }
    43  
    44  // AddToStatCounter a CopyOption add to stat counter
    45  func AddToStatCounter(sc stats.Counter) CopyOption {
    46  	return func(handler *copyHandler) {
    47  		handler.onData = append(handler.onData, func(b MultiBuffer) {
    48  			if sc != nil {
    49  				sc.Add(int64(b.Len()))
    50  			}
    51  		})
    52  	}
    53  }
    54  
    55  type readError struct {
    56  	error
    57  }
    58  
    59  func (e readError) Error() string {
    60  	return e.error.Error()
    61  }
    62  
    63  func (e readError) Unwrap() error {
    64  	return e.error
    65  }
    66  
    67  // IsReadError returns true if the error in Copy() comes from reading.
    68  func IsReadError(err error) bool {
    69  	_, ok := err.(readError)
    70  	return ok
    71  }
    72  
    73  type writeError struct {
    74  	error
    75  }
    76  
    77  func (e writeError) Error() string {
    78  	return e.error.Error()
    79  }
    80  
    81  func (e writeError) Unwrap() error {
    82  	return e.error
    83  }
    84  
    85  // IsWriteError returns true if the error in Copy() comes from writing.
    86  func IsWriteError(err error) bool {
    87  	_, ok := err.(writeError)
    88  	return ok
    89  }
    90  
    91  func copyInternal(reader Reader, writer Writer, handler *copyHandler) error {
    92  	for {
    93  		buffer, err := reader.ReadMultiBuffer()
    94  		if !buffer.IsEmpty() {
    95  			for _, handler := range handler.onData {
    96  				handler(buffer)
    97  			}
    98  
    99  			if werr := writer.WriteMultiBuffer(buffer); werr != nil {
   100  				return writeError{werr}
   101  			}
   102  		}
   103  
   104  		if err != nil {
   105  			return readError{err}
   106  		}
   107  	}
   108  }
   109  
   110  // Copy dumps all payload from reader to writer or stops when an error occurs. It returns nil when EOF.
   111  func Copy(reader Reader, writer Writer, options ...CopyOption) error {
   112  	var handler copyHandler
   113  	for _, option := range options {
   114  		option(&handler)
   115  	}
   116  	err := copyInternal(reader, writer, &handler)
   117  	if err != nil && errors.Cause(err) != io.EOF {
   118  		return err
   119  	}
   120  	return nil
   121  }
   122  
   123  var ErrNotTimeoutReader = newError("not a TimeoutReader")
   124  
   125  func CopyOnceTimeout(reader Reader, writer Writer, timeout time.Duration) error {
   126  	timeoutReader, ok := reader.(TimeoutReader)
   127  	if !ok {
   128  		return ErrNotTimeoutReader
   129  	}
   130  	mb, err := timeoutReader.ReadMultiBufferTimeout(timeout)
   131  	if err != nil {
   132  		return err
   133  	}
   134  	return writer.WriteMultiBuffer(mb)
   135  }