boundedreadwriteseeker.go (4174B)
1 package rifs 2 3 import ( 4 "errors" 5 "io" 6 "os" 7 8 "github.com/dsoprea/go-logging" 9 ) 10 11 var ( 12 // ErrSeekBeyondBound is returned when a seek is requested beyond the 13 // statically-given file-size. No writes or seeks beyond boundaries are 14 // supported with a statically-given file size. 15 ErrSeekBeyondBound = errors.New("seek beyond boundary") 16 ) 17 18 // BoundedReadWriteSeeker is a thin filter that ensures that no seeks can be done 19 // to offsets smaller than the one we were given. This supports libraries that 20 // might be expecting to read from the front of the stream being used on data 21 // that is in the middle of a stream instead. 22 type BoundedReadWriteSeeker struct { 23 io.ReadWriteSeeker 24 25 currentOffset int64 26 minimumOffset int64 27 28 staticFileSize int64 29 } 30 31 // NewBoundedReadWriteSeeker returns a new BoundedReadWriteSeeker instance. 32 func NewBoundedReadWriteSeeker(rws io.ReadWriteSeeker, minimumOffset int64, staticFileSize int64) (brws *BoundedReadWriteSeeker, err error) { 33 defer func() { 34 if state := recover(); state != nil { 35 err = log.Wrap(state.(error)) 36 } 37 }() 38 39 if minimumOffset < 0 { 40 log.Panicf("BoundedReadWriteSeeker minimum offset must be zero or larger: (%d)", minimumOffset) 41 } 42 43 // We'll always started at a relative offset of zero. 44 _, err = rws.Seek(minimumOffset, os.SEEK_SET) 45 log.PanicIf(err) 46 47 brws = &BoundedReadWriteSeeker{ 48 ReadWriteSeeker: rws, 49 50 currentOffset: 0, 51 minimumOffset: minimumOffset, 52 53 staticFileSize: staticFileSize, 54 } 55 56 return brws, nil 57 } 58 59 // Seek moves the offset to the given offset. Prevents offset from ever being 60 // moved left of `brws.minimumOffset`. 61 func (brws *BoundedReadWriteSeeker) Seek(offset int64, whence int) (updatedOffset int64, err error) { 62 defer func() { 63 if state := recover(); state != nil { 64 err = log.Wrap(state.(error)) 65 } 66 }() 67 68 fileSize := brws.staticFileSize 69 70 // If we weren't given a static file-size, look it up whenever it is needed. 71 if whence == os.SEEK_END && fileSize == 0 { 72 realFileSizeRaw, err := brws.ReadWriteSeeker.Seek(0, os.SEEK_END) 73 log.PanicIf(err) 74 75 fileSize = realFileSizeRaw - brws.minimumOffset 76 } 77 78 updatedOffset, err = CalculateSeek(brws.currentOffset, offset, whence, fileSize) 79 log.PanicIf(err) 80 81 if brws.staticFileSize != 0 && updatedOffset > brws.staticFileSize { 82 //updatedOffset = int64(brws.staticFileSize) 83 84 // NOTE(dustin): Presumably, this will only be disruptive to writes that are beyond the boundaries, which, if we're being used at all, should already account for the boundary and prevent this error from ever happening. So, time will tell how disruptive this is. 85 return 0, ErrSeekBeyondBound 86 } 87 88 if updatedOffset != brws.currentOffset { 89 updatedRealOffset := updatedOffset + brws.minimumOffset 90 91 _, err = brws.ReadWriteSeeker.Seek(updatedRealOffset, os.SEEK_SET) 92 log.PanicIf(err) 93 94 brws.currentOffset = updatedOffset 95 } 96 97 return updatedOffset, nil 98 } 99 100 // Read forwards writes to the inner RWS. 101 func (brws *BoundedReadWriteSeeker) Read(buffer []byte) (readCount int, err error) { 102 defer func() { 103 if state := recover(); state != nil { 104 err = log.Wrap(state.(error)) 105 } 106 }() 107 108 if brws.staticFileSize != 0 { 109 availableCount := brws.staticFileSize - brws.currentOffset 110 if availableCount == 0 { 111 return 0, io.EOF 112 } 113 114 if int64(len(buffer)) > availableCount { 115 buffer = buffer[:availableCount] 116 } 117 } 118 119 readCount, err = brws.ReadWriteSeeker.Read(buffer) 120 brws.currentOffset += int64(readCount) 121 122 if err != nil { 123 if err == io.EOF { 124 return 0, err 125 } 126 127 log.Panic(err) 128 } 129 130 return readCount, nil 131 } 132 133 // Write forwards writes to the inner RWS. 134 func (brws *BoundedReadWriteSeeker) Write(buffer []byte) (writtenCount int, err error) { 135 defer func() { 136 if state := recover(); state != nil { 137 err = log.Wrap(state.(error)) 138 } 139 }() 140 141 if brws.staticFileSize != 0 { 142 log.Panicf("writes can not be performed if a static file-size was given") 143 } 144 145 writtenCount, err = brws.ReadWriteSeeker.Write(buffer) 146 brws.currentOffset += int64(writtenCount) 147 148 log.PanicIf(err) 149 150 return writtenCount, nil 151 } 152 153 // MinimumOffset returns the configured minimum-offset. 154 func (brws *BoundedReadWriteSeeker) MinimumOffset() int64 { 155 return brws.minimumOffset 156 }