storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/pkg/s3select/progress.go (about) 1 /* 2 * MinIO Cloud Storage, (C) 2019 MinIO, Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package s3select 18 19 import ( 20 "compress/bzip2" 21 "errors" 22 "fmt" 23 "io" 24 "sync" 25 "sync/atomic" 26 27 gzip "github.com/klauspost/pgzip" 28 ) 29 30 type countUpReader struct { 31 reader io.Reader 32 bytesRead int64 33 } 34 35 func (r *countUpReader) Read(p []byte) (n int, err error) { 36 n, err = r.reader.Read(p) 37 atomic.AddInt64(&r.bytesRead, int64(n)) 38 return n, err 39 } 40 41 func (r *countUpReader) BytesRead() int64 { 42 if r == nil { 43 return 0 44 } 45 return atomic.LoadInt64(&r.bytesRead) 46 } 47 48 func newCountUpReader(reader io.Reader) *countUpReader { 49 return &countUpReader{ 50 reader: reader, 51 } 52 } 53 54 type progressReader struct { 55 rc io.ReadCloser 56 scannedReader *countUpReader 57 processedReader *countUpReader 58 59 closedMu sync.Mutex 60 gzr *gzip.Reader 61 closed bool 62 } 63 64 func (pr *progressReader) Read(p []byte) (n int, err error) { 65 // This ensures that Close will block until Read has completed. 66 // This allows another goroutine to close the reader. 67 pr.closedMu.Lock() 68 defer pr.closedMu.Unlock() 69 if pr.closed { 70 return 0, errors.New("progressReader: read after Close") 71 } 72 return pr.processedReader.Read(p) 73 } 74 75 func (pr *progressReader) Close() error { 76 pr.closedMu.Lock() 77 defer pr.closedMu.Unlock() 78 if pr.closed { 79 return nil 80 } 81 pr.closed = true 82 if pr.gzr != nil { 83 pr.gzr.Close() 84 } 85 return pr.rc.Close() 86 } 87 88 func (pr *progressReader) Stats() (bytesScanned, bytesProcessed int64) { 89 if pr == nil { 90 return 0, 0 91 } 92 return pr.scannedReader.BytesRead(), pr.processedReader.BytesRead() 93 } 94 95 func newProgressReader(rc io.ReadCloser, compType CompressionType) (*progressReader, error) { 96 if rc == nil { 97 return nil, errors.New("newProgressReader: nil reader provided") 98 } 99 scannedReader := newCountUpReader(rc) 100 pr := progressReader{ 101 rc: rc, 102 scannedReader: scannedReader, 103 } 104 var err error 105 var r io.Reader 106 107 switch compType { 108 case noneType: 109 r = scannedReader 110 case gzipType: 111 pr.gzr, err = gzip.NewReader(scannedReader) 112 if err != nil { 113 if errors.Is(err, gzip.ErrHeader) || errors.Is(err, gzip.ErrChecksum) { 114 return nil, errInvalidGZIPCompressionFormat(err) 115 } 116 return nil, errTruncatedInput(err) 117 } 118 r = pr.gzr 119 case bzip2Type: 120 r = bzip2.NewReader(scannedReader) 121 default: 122 return nil, errInvalidCompressionFormat(fmt.Errorf("unknown compression type '%v'", compType)) 123 } 124 pr.processedReader = newCountUpReader(r) 125 126 return &pr, nil 127 }