func (o *Object) setOffset(bytesRead int64) error {
	o.currOffset += bytesRead
	if o.objectInfo.Size > -1 && o.currOffset >= o.objectInfo.Size {
		return io.EOF
	}
	return nil
}
setOffset - handles the setting of offsets for
Read/ReadAt/Seek requests.
package minio
import (
	"context"
	"errors"
	"fmt"
	"io"
	"net/http"
	"sync"
	"github.com/minio/minio-go/v6/pkg/s3utils"
)
func (c Client) GetObject(bucketName, objectName string, opts GetObjectOptions) (*Object, error) {
	return c.getObjectWithContext(context.Background(), bucketName, objectName, opts)
}
func (c Client) getObjectWithContext(ctx context.Context, bucketName, objectName string, opts GetObjectOptions) (*Object, error) {
	if err := s3utils.CheckValidBucketName(bucketName); err != nil {
		return nil, err
	}
	if err := s3utils.CheckValidObjectName(objectName); err != nil {
		return nil, err
	}
	var snowball bool
	if location, ok := c.bucketLocCache.Get(bucketName); ok {
		if location == "snowball" {
			snowball = true
		}
	}
	var httpReader io.ReadCloser
	var objectInfo ObjectInfo
	var err error
	reqCh := make(chan getRequest)
	resCh := make(chan getResponse)
	doneCh := make(chan struct{})
	go func() {
		defer close(reqCh)
		defer close(resCh)
		var etag string
		for {
			select {
			case <-doneCh:
				if httpReader != nil {
					httpReader.Close()
				}
				return
			case req := <-reqCh:
				if req.isFirstReq {
					if req.isReadOp {
						if req.isReadAt {
							opts.SetRange(req.Offset, req.Offset+int64(len(req.Buffer))-1)
						} else if req.Offset > 0 {
							opts.SetRange(req.Offset, 0)
						}
						httpReader, objectInfo, _, err = c.getObject(ctx, bucketName, objectName, opts)
						if err != nil {
							resCh <- getResponse{Error: err}
							return
						}
						etag = objectInfo.ETag
						size, err := io.ReadFull(httpReader, req.Buffer)
						if size > 0 && err == io.ErrUnexpectedEOF {
							err = io.EOF
						}
						resCh <- getResponse{
							objectInfo: objectInfo,
							Size:       int(size),
							Error:      err,
							didRead:    true,
						}
					} else {
						delete(opts.headers, "Range")
						objectInfo, err = c.statObject(ctx, bucketName, objectName, StatObjectOptions{opts})
						if err != nil {
							resCh <- getResponse{
								Error: err,
							}
							return
						}
						etag = objectInfo.ETag
						resCh <- getResponse{
							objectInfo: objectInfo,
						}
					}
				} else if req.settingObjectInfo {  
					delete(opts.headers, "Range")
					if etag != "" && !snowball {
						opts.SetMatchETag(etag)
					}
					objectInfo, err := c.statObject(ctx, bucketName, objectName, StatObjectOptions{opts})
					if err != nil {
						resCh <- getResponse{
							Error: err,
						}
						return
					}
					resCh <- getResponse{
						objectInfo: objectInfo,
					}
				} else {
					if req.DidOffsetChange || !req.beenRead {
						if etag != "" && !snowball {
							opts.SetMatchETag(etag)
						}
						if httpReader != nil {
							httpReader.Close()
						}
						if req.isReadAt {
							opts.SetRange(req.Offset, req.Offset+int64(len(req.Buffer))-1)
						} else if req.Offset > 0 {  
							opts.SetRange(req.Offset, 0)
						}
						httpReader, objectInfo, _, err = c.getObject(ctx, bucketName, objectName, opts)
						if err != nil {
							resCh <- getResponse{
								Error: err,
							}
							return
						}
					}
					size, err := io.ReadFull(httpReader, req.Buffer)
					if err == io.ErrUnexpectedEOF {
						err = io.EOF
					}
					resCh <- getResponse{
						Size:       int(size),
						Error:      err,
						didRead:    true,
						objectInfo: objectInfo,
					}
				}
			}
		}
	}()
	return newObject(reqCh, resCh, doneCh), nil
}
type getRequest struct {
	Buffer            []byte
	Offset            int64  
	DidOffsetChange   bool   
	beenRead          bool   
	isReadAt          bool   
	isReadOp          bool   
	isFirstReq        bool   
	settingObjectInfo bool   
}
type getResponse struct {
	Size       int
	Error      error
	didRead    bool        
	objectInfo ObjectInfo  
}
type Object struct {
	mutex *sync.Mutex
	reqCh      chan<- getRequest
	resCh      <-chan getResponse
	doneCh     chan<- struct{}
	currOffset int64
	objectInfo ObjectInfo
	seekData bool
	isClosed bool
	isStarted bool
	prevErr error
	beenRead bool
	objectInfoSet bool
}
func (o *Object) doGetRequest(request getRequest) (getResponse, error) {
	o.reqCh <- request
	response := <-o.resCh
	if response.Error != nil {
		return response, response.Error
	}
	if !o.isStarted {
		o.isStarted = true
	}
	if !o.objectInfoSet && !request.isReadAt {
		o.objectInfo = response.objectInfo
		o.objectInfoSet = true
	}
	if !o.beenRead {
		o.beenRead = response.didRead
	}
	o.seekData = false
	return response, nil
}