github.com/containers/podman/v4@v4.9.4/libpod/logs/reversereader/reversereader.go (about)

     1  package reversereader
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"io"
     7  	"os"
     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 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 "", fmt.Errorf("at beginning of file: %w", io.EOF)
    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.Is(err, io.EOF) {
    58  		return "", err
    59  	}
    60  	// Move the offset one pagesize up
    61  	r.offset -= r.readSize
    62  	return string(b[:n]), nil
    63  }