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 }