gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

hook-reader.go (2661B)


      1 /*
      2  * MinIO Go Library for Amazon S3 Compatible Cloud Storage
      3  * Copyright 2015-2017 MinIO, Inc.
      4  *
      5  * Licensed under the Apache License, Version 2.0 (the "License");
      6  * you may not use this file except in compliance with the License.
      7  * You may obtain a copy of the License at
      8  *
      9  *     http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  * Unless required by applicable law or agreed to in writing, software
     12  * distributed under the License is distributed on an "AS IS" BASIS,
     13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  * See the License for the specific language governing permissions and
     15  * limitations under the License.
     16  */
     17 
     18 package minio
     19 
     20 import (
     21 	"fmt"
     22 	"io"
     23 	"sync"
     24 )
     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 	mu     sync.RWMutex
     32 	source io.Reader
     33 	hook   io.Reader
     34 }
     35 
     36 // Seek implements io.Seeker. Seeks source first, and if necessary
     37 // seeks hook if Seek method is appropriately found.
     38 func (hr *hookReader) Seek(offset int64, whence int) (n int64, err error) {
     39 	hr.mu.Lock()
     40 	defer hr.mu.Unlock()
     41 
     42 	// Verify for source has embedded Seeker, use it.
     43 	sourceSeeker, ok := hr.source.(io.Seeker)
     44 	if ok {
     45 		n, err = sourceSeeker.Seek(offset, whence)
     46 		if err != nil {
     47 			return 0, err
     48 		}
     49 	}
     50 
     51 	if hr.hook != nil {
     52 		// Verify if hook has embedded Seeker, use it.
     53 		hookSeeker, ok := hr.hook.(io.Seeker)
     54 		if ok {
     55 			var m int64
     56 			m, err = hookSeeker.Seek(offset, whence)
     57 			if err != nil {
     58 				return 0, err
     59 			}
     60 			if n != m {
     61 				return 0, fmt.Errorf("hook seeker seeked %d bytes, expected source %d bytes", m, n)
     62 			}
     63 		}
     64 	}
     65 
     66 	return n, nil
     67 }
     68 
     69 // Read implements io.Reader. Always reads from the source, the return
     70 // value 'n' number of bytes are reported through the hook. Returns
     71 // error for all non io.EOF conditions.
     72 func (hr *hookReader) Read(b []byte) (n int, err error) {
     73 	hr.mu.RLock()
     74 	defer hr.mu.RUnlock()
     75 
     76 	n, err = hr.source.Read(b)
     77 	if err != nil && err != io.EOF {
     78 		return n, err
     79 	}
     80 	if hr.hook != nil {
     81 		// Progress the hook with the total read bytes from the source.
     82 		if _, herr := hr.hook.Read(b[:n]); herr != nil {
     83 			if herr != io.EOF {
     84 				return n, herr
     85 			}
     86 		}
     87 	}
     88 	return n, err
     89 }
     90 
     91 // newHook returns a io.ReadSeeker which implements hookReader that
     92 // reports the data read from the source to the hook.
     93 func newHook(source, hook io.Reader) io.Reader {
     94 	if hook == nil {
     95 		return &hookReader{source: source}
     96 	}
     97 	return &hookReader{
     98 		source: source,
     99 		hook:   hook,
    100 	}
    101 }