github.com/grailbio/base@v0.0.11/recordio/deprecated/range.go (about)

     1  // Copyright 2017 GRAIL, Inc. All rights reserved.
     2  // Use of this source code is governed by the Apache-2.0
     3  // license that can be found in the LICENSE file.
     4  
     5  package deprecated
     6  
     7  import "io"
     8  
     9  // RangeReader represents an io.ReadSeeker that operates over a restricted
    10  // range of the supplied ReadSeeker. Reading and Seeking are consequently
    11  // relative to the start of the range used to create the RangeReader.
    12  type RangeReader struct {
    13  	rs              io.ReadSeeker
    14  	start, pos, end int64
    15  }
    16  
    17  // NewRangeReader returns a new RangedReader.
    18  func NewRangeReader(rs io.ReadSeeker, offset, length int64) (io.ReadSeeker, error) {
    19  	_, err := rs.Seek(offset, io.SeekStart)
    20  	if err != nil {
    21  		return nil, err
    22  	}
    23  	return &RangeReader{
    24  		rs:    rs,
    25  		start: offset,
    26  		pos:   offset,
    27  		end:   offset + length,
    28  	}, nil
    29  }
    30  
    31  // Read implements io.Read.
    32  func (rr *RangeReader) Read(buf []byte) (int, error) {
    33  	var eof error
    34  	size := int64(len(buf))
    35  	if rr.pos+size >= rr.end {
    36  		size = rr.end - rr.pos
    37  		eof = io.EOF
    38  	}
    39  	n, err := rr.rs.Read(buf[:size])
    40  	if err == nil {
    41  		err = eof
    42  	}
    43  	rr.pos += int64(n)
    44  	return n, err
    45  }
    46  
    47  // Seek implements io.Seek.
    48  func (rr *RangeReader) Seek(offset int64, whence int) (int64, error) {
    49  	pos := rr.start
    50  	switch whence {
    51  	case io.SeekStart:
    52  		pos += offset
    53  	case io.SeekCurrent:
    54  		pos = rr.pos + offset
    55  	case io.SeekEnd:
    56  		if offset > 0 {
    57  			offset = 0
    58  		}
    59  		pos = rr.end + offset
    60  	}
    61  	if pos > rr.end {
    62  		rr.pos = rr.end
    63  		return pos, nil
    64  	}
    65  	n, err := rr.rs.Seek(pos, io.SeekStart)
    66  	if err == nil {
    67  		rr.pos = n
    68  	}
    69  	return n - rr.start, err
    70  }