github.com/pingcap/br@v5.3.0-alpha.0.20220125034240-ec59c7b6ce30+incompatible/pkg/storage/writer.go (about)

     1  package storage
     2  
     3  import (
     4  	"bytes"
     5  	"compress/gzip"
     6  	"context"
     7  	"io"
     8  
     9  	"github.com/pingcap/errors"
    10  )
    11  
    12  // CompressType represents the type of compression.
    13  type CompressType uint8
    14  
    15  const (
    16  	// NoCompression won't compress given bytes.
    17  	NoCompression CompressType = iota
    18  	// Gzip will compress given bytes in gzip format.
    19  	Gzip
    20  )
    21  
    22  type flusher interface {
    23  	Flush() error
    24  }
    25  
    26  type emptyFlusher struct{}
    27  
    28  func (e *emptyFlusher) Flush() error {
    29  	return nil
    30  }
    31  
    32  type interceptBuffer interface {
    33  	io.WriteCloser
    34  	flusher
    35  	Len() int
    36  	Cap() int
    37  	Bytes() []byte
    38  	Reset()
    39  }
    40  
    41  func newInterceptBuffer(chunkSize int, compressType CompressType) interceptBuffer {
    42  	if compressType == NoCompression {
    43  		return newNoCompressionBuffer(chunkSize)
    44  	}
    45  	return newSimpleCompressBuffer(chunkSize, compressType)
    46  }
    47  
    48  func newCompressWriter(compressType CompressType, w io.Writer) simpleCompressWriter {
    49  	switch compressType {
    50  	case Gzip:
    51  		return gzip.NewWriter(w)
    52  	default:
    53  		return nil
    54  	}
    55  }
    56  
    57  func newCompressReader(compressType CompressType, r io.Reader) (io.ReadCloser, error) {
    58  	switch compressType {
    59  	case Gzip:
    60  		return gzip.NewReader(r)
    61  	default:
    62  		return nil, nil
    63  	}
    64  }
    65  
    66  type noCompressionBuffer struct {
    67  	*bytes.Buffer
    68  }
    69  
    70  func (b *noCompressionBuffer) Flush() error {
    71  	return nil
    72  }
    73  
    74  func (b *noCompressionBuffer) Close() error {
    75  	return nil
    76  }
    77  
    78  func newNoCompressionBuffer(chunkSize int) *noCompressionBuffer {
    79  	return &noCompressionBuffer{bytes.NewBuffer(make([]byte, 0, chunkSize))}
    80  }
    81  
    82  type simpleCompressWriter interface {
    83  	io.WriteCloser
    84  	flusher
    85  }
    86  
    87  type simpleCompressBuffer struct {
    88  	*bytes.Buffer
    89  	compressWriter simpleCompressWriter
    90  	len            int
    91  	cap            int
    92  }
    93  
    94  func (b *simpleCompressBuffer) Write(p []byte) (int, error) {
    95  	written, err := b.compressWriter.Write(p)
    96  	b.len += written
    97  	return written, errors.Trace(err)
    98  }
    99  
   100  func (b *simpleCompressBuffer) Len() int {
   101  	return b.len
   102  }
   103  
   104  func (b *simpleCompressBuffer) Cap() int {
   105  	return b.cap
   106  }
   107  
   108  func (b *simpleCompressBuffer) Reset() {
   109  	b.len = 0
   110  	b.Buffer.Reset()
   111  }
   112  
   113  func (b *simpleCompressBuffer) Flush() error {
   114  	return b.compressWriter.Flush()
   115  }
   116  
   117  func (b *simpleCompressBuffer) Close() error {
   118  	return b.compressWriter.Close()
   119  }
   120  
   121  func newSimpleCompressBuffer(chunkSize int, compressType CompressType) *simpleCompressBuffer {
   122  	bf := bytes.NewBuffer(make([]byte, 0, chunkSize))
   123  	return &simpleCompressBuffer{
   124  		Buffer:         bf,
   125  		len:            0,
   126  		cap:            chunkSize,
   127  		compressWriter: newCompressWriter(compressType, bf),
   128  	}
   129  }
   130  
   131  type bufferedWriter struct {
   132  	buf    interceptBuffer
   133  	writer ExternalFileWriter
   134  }
   135  
   136  func (u *bufferedWriter) Write(ctx context.Context, p []byte) (int, error) {
   137  	bytesWritten := 0
   138  	for u.buf.Len()+len(p) > u.buf.Cap() {
   139  		// We won't fit p in this chunk
   140  
   141  		// Is this chunk full?
   142  		chunkToFill := u.buf.Cap() - u.buf.Len()
   143  		if chunkToFill > 0 {
   144  			// It's not full so we write enough of p to fill it
   145  			prewrite := p[0:chunkToFill]
   146  			w, err := u.buf.Write(prewrite)
   147  			bytesWritten += w
   148  			if err != nil {
   149  				return bytesWritten, errors.Trace(err)
   150  			}
   151  			p = p[w:]
   152  		}
   153  		u.buf.Flush()
   154  		err := u.uploadChunk(ctx)
   155  		if err != nil {
   156  			return 0, errors.Trace(err)
   157  		}
   158  	}
   159  	w, err := u.buf.Write(p)
   160  	bytesWritten += w
   161  	return bytesWritten, errors.Trace(err)
   162  }
   163  
   164  func (u *bufferedWriter) uploadChunk(ctx context.Context) error {
   165  	if u.buf.Len() == 0 {
   166  		return nil
   167  	}
   168  	b := u.buf.Bytes()
   169  	u.buf.Reset()
   170  	_, err := u.writer.Write(ctx, b)
   171  	return errors.Trace(err)
   172  }
   173  
   174  func (u *bufferedWriter) Close(ctx context.Context) error {
   175  	u.buf.Close()
   176  	err := u.uploadChunk(ctx)
   177  	if err != nil {
   178  		return errors.Trace(err)
   179  	}
   180  	return u.writer.Close(ctx)
   181  }
   182  
   183  // NewUploaderWriter wraps the Writer interface over an uploader.
   184  func NewUploaderWriter(writer ExternalFileWriter, chunkSize int, compressType CompressType) ExternalFileWriter {
   185  	return newBufferedWriter(writer, chunkSize, compressType)
   186  }
   187  
   188  // newBufferedWriter is used to build a buffered writer.
   189  func newBufferedWriter(writer ExternalFileWriter, chunkSize int, compressType CompressType) *bufferedWriter {
   190  	return &bufferedWriter{
   191  		writer: writer,
   192  		buf:    newInterceptBuffer(chunkSize, compressType),
   193  	}
   194  }
   195  
   196  // BytesWriter is a Writer implementation on top of bytes.Buffer that is useful for testing.
   197  type BytesWriter struct {
   198  	buf *bytes.Buffer
   199  }
   200  
   201  // Write delegates to bytes.Buffer.
   202  func (u *BytesWriter) Write(ctx context.Context, p []byte) (int, error) {
   203  	return u.buf.Write(p)
   204  }
   205  
   206  // Close delegates to bytes.Buffer.
   207  func (u *BytesWriter) Close(ctx context.Context) error {
   208  	// noop
   209  	return nil
   210  }
   211  
   212  // Bytes delegates to bytes.Buffer.
   213  func (u *BytesWriter) Bytes() []byte {
   214  	return u.buf.Bytes()
   215  }
   216  
   217  // String delegates to bytes.Buffer.
   218  func (u *BytesWriter) String() string {
   219  	return u.buf.String()
   220  }
   221  
   222  // Reset delegates to bytes.Buffer.
   223  func (u *BytesWriter) Reset() {
   224  	u.buf.Reset()
   225  }
   226  
   227  // NewBufferWriter creates a Writer that simply writes to a buffer (useful for testing).
   228  func NewBufferWriter() *BytesWriter {
   229  	return &BytesWriter{buf: &bytes.Buffer{}}
   230  }