github.com/minio/mc@v0.0.0-20240507152021-646712d5e5fb/pkg/hookreader/hookreader.go (about) 1 // Copyright (c) 2015-2021 MinIO, Inc. 2 // 3 // This file is part of MinIO Object Storage stack 4 // 5 // This program is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Affero General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // This program is distributed in the hope that it will be useful 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Affero General Public License for more details. 14 // 15 // You should have received a copy of the GNU Affero General Public License 16 // along with this program. If not, see <http://www.gnu.org/licenses/>. 17 18 // Package hookreader hooks additional reader in the source 19 // stream. It is useful for making progress bars. Second reader is 20 // appropriately notified about the exact number of bytes read from 21 // the primary source on each Read operation. 22 package hookreader 23 24 import "io" 25 26 // hookReader hooks additional reader in the source stream. It is 27 // useful for making progress bars. Second reader is appropriately 28 // notified about the exact number of bytes read from the primary 29 // source on each Read operation. 30 type hookReader struct { 31 source io.Reader 32 hook io.Reader 33 } 34 35 // Seek implements io.Seeker. Seeks source first, and if necessary 36 // seeks hook if Seek method is appropriately found. 37 func (hr *hookReader) Seek(offset int64, whence int) (n int64, err error) { 38 // Verify for source has embedded Seeker, use it. 39 sourceSeeker, ok := hr.source.(io.Seeker) 40 if ok { 41 return sourceSeeker.Seek(offset, whence) 42 } 43 // Verify if hook has embedded Seeker, use it. 44 hookSeeker, ok := hr.hook.(io.Seeker) 45 if ok { 46 return hookSeeker.Seek(offset, whence) 47 } 48 return n, nil 49 } 50 51 // Read implements io.Reader. Always reads from the source, the return 52 // value 'n' number of bytes are reported through the hook. Returns 53 // error for all non io.EOF conditions. 54 func (hr *hookReader) Read(b []byte) (n int, err error) { 55 n, err = hr.source.Read(b) 56 if err != nil && err != io.EOF { 57 return n, err 58 } 59 // Progress the hook with the total read bytes from the source. 60 if _, herr := hr.hook.Read(b[:n]); herr != nil { 61 if herr != io.EOF { 62 return n, herr 63 } 64 } 65 return n, err 66 } 67 68 // NewHook returns a io.Reader which implements hookReader that 69 // reports the data read from the source to the hook. 70 func NewHook(source, hook io.Reader) io.Reader { 71 if hook == nil { 72 return source 73 } 74 return &hookReader{source, hook} 75 }