github.com/searKing/golang/go@v1.2.117/io/dynamic_read_seeker.go (about)

     1  // Copyright 2020 The searKing Author. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package io
     6  
     7  import (
     8  	"io"
     9  	"sync"
    10  )
    11  
    12  // DynamicReadSeeker returns a ReadSeeker that reads from r got by getter at an offset.
    13  // The underlying implementation is a *dynamicReadSeeker.
    14  func DynamicReadSeeker(getter func(off int64) (io.Reader, error), size int64) io.ReadSeeker {
    15  	return &dynamicReadSeeker{
    16  		getter:    getter,
    17  		totalSize: size,
    18  	}
    19  }
    20  
    21  // A dynamicReadSeeker reads from r got by getter at an offset.
    22  type dynamicReadSeeker struct {
    23  	getter    func(off int64) (io.Reader, error)
    24  	totalSize int64 // make no sense if rs implements io.Seeker
    25  
    26  	rs         io.Reader // underlying readSeeker
    27  	lastOffset int64     // make no sense if rs implements io.Seeker
    28  	once       sync.Once
    29  }
    30  
    31  func (l *dynamicReadSeeker) lazyLoad() {
    32  	l.once.Do(func() {
    33  		if l.getter == nil {
    34  			return
    35  		}
    36  
    37  		if l.rs == nil {
    38  			l.rs, _ = l.getter(0)
    39  			l.lastOffset = 0
    40  		}
    41  	})
    42  }
    43  
    44  func (l *dynamicReadSeeker) Read(p []byte) (n int, err error) {
    45  	l.lazyLoad()
    46  
    47  	if l.rs == nil {
    48  		return 0, io.EOF
    49  	}
    50  
    51  	if l.lastOffset >= l.totalSize {
    52  		return 0, io.EOF
    53  	}
    54  
    55  	n, err = l.rs.Read(p)
    56  	if n >= 0 {
    57  		l.lastOffset += int64(n)
    58  	}
    59  
    60  	return
    61  }
    62  
    63  func (l *dynamicReadSeeker) Seek(offset int64, whence int) (n int64, err error) {
    64  	l.lazyLoad()
    65  	if l.rs == nil {
    66  		return 0, errSeeker
    67  	}
    68  	if seeker, ok := l.rs.(io.Seeker); ok {
    69  		n, err = seeker.Seek(offset, whence)
    70  		l.lastOffset = n
    71  		return
    72  	}
    73  
    74  	// speed up
    75  	if whence == io.SeekCurrent && offset == 0 {
    76  		n = l.lastOffset
    77  		return n, nil
    78  	}
    79  
    80  	if whence == io.SeekStart && offset == l.lastOffset {
    81  		n = l.lastOffset
    82  		return n, nil
    83  	}
    84  
    85  	switch whence {
    86  	case io.SeekStart:
    87  		break
    88  	case io.SeekCurrent:
    89  		offset += l.lastOffset
    90  	case io.SeekEnd:
    91  		offset += l.totalSize
    92  	}
    93  
    94  	if offset >= l.totalSize {
    95  		l.lastOffset = offset
    96  		return offset, nil
    97  	}
    98  
    99  	if err := l.Close(); err != nil {
   100  		return 0, err
   101  	}
   102  
   103  	if l.getter == nil {
   104  		return 0, errSeeker
   105  	}
   106  
   107  	l.rs, err = l.getter(offset)
   108  	if err != nil {
   109  		return 0, err
   110  	}
   111  	if l.rs == nil {
   112  		return 0, errSeeker
   113  	}
   114  
   115  	l.lastOffset = offset
   116  	return offset, nil
   117  }
   118  
   119  func (l *dynamicReadSeeker) Close() error {
   120  	defer func() {
   121  		l.lastOffset = 0
   122  	}()
   123  
   124  	if l.rs == nil {
   125  		return nil
   126  	}
   127  	defer func() {
   128  		l.rs = nil
   129  	}()
   130  
   131  	if closer, ok := l.rs.(io.Closer); ok {
   132  		return closer.Close()
   133  	}
   134  	return nil
   135  }