gtsocial-umbx

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

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 }