github.com/containers/libpod@v1.9.4-0.20220419124438-4284fd425507/libpod/logs/reversereader/reversereader.go (about)

     1  package reversereader
     2  
     3  import (
     4  	"io"
     5  	"os"
     6  
     7  	"github.com/pkg/errors"
     8  )
     9  
    10  // ReverseReader structure for reading a file backwards
    11  type ReverseReader struct {
    12  	reader   *os.File
    13  	offset   int64
    14  	readSize int64
    15  }
    16  
    17  // NewReverseReader returns a reader that reads from the end of a file
    18  // rather than the beginning.  It sets the readsize to pagesize and determines
    19  // the first offset using using modulus.
    20  func NewReverseReader(reader *os.File) (*ReverseReader, error) {
    21  	// pagesize should be safe for memory use and file reads should be on page
    22  	// boundaries as well
    23  	pageSize := int64(os.Getpagesize())
    24  	stat, err := reader.Stat()
    25  	if err != nil {
    26  		return nil, err
    27  	}
    28  	// figure out the last page boundary
    29  	remainder := stat.Size() % pageSize
    30  	end, err := reader.Seek(0, 2)
    31  	if err != nil {
    32  		return nil, err
    33  	}
    34  	// set offset (starting position) to the last page boundary or
    35  	// zero if fits in one page
    36  	startOffset := end - remainder
    37  	if startOffset < 0 {
    38  		startOffset = 0
    39  	}
    40  	rr := ReverseReader{
    41  		reader:   reader,
    42  		offset:   startOffset,
    43  		readSize: pageSize,
    44  	}
    45  	return &rr, nil
    46  }
    47  
    48  // ReverseReader reads from a given offset to the previous offset and
    49  // then sets the newoff set one pagesize less than the previous read.
    50  func (r *ReverseReader) Read() (string, error) {
    51  	if r.offset < 0 {
    52  		return "", errors.Wrap(io.EOF, "at beginning of file")
    53  	}
    54  	// Read from given offset
    55  	b := make([]byte, r.readSize)
    56  	n, err := r.reader.ReadAt(b, r.offset)
    57  	if err != nil && errors.Cause(err) != io.EOF {
    58  		return "", err
    59  	}
    60  	if int64(n) < r.readSize {
    61  		b = b[0:n]
    62  	}
    63  	// Set to the next page boundary
    64  	r.offset = -r.readSize
    65  	return string(b), nil
    66  }