...

Source file src/github.com/dsoprea/go-utility/v2/filesystem/boundedreadwriteseeker.go

Documentation: github.com/dsoprea/go-utility/v2/filesystem

     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  }
   157  

View as plain text