talos/pkg/tail/tail.go
Andrey Smirnov 8560fb9662 chore: enable nlreturn linter
Most of the fixes were automatically applied.

Signed-off-by: Andrey Smirnov <smirnov.andrey@gmail.com>
2020-11-09 06:48:07 -08:00

94 lines
1.7 KiB
Go

// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package tail
import (
"bytes"
"fmt"
"io"
)
// Window is the read window size for tail scanning.
const Window = 4096
// SeekLines seeks the passed io.ReadSeeker so that it's -N lines from the tail.
//
// SeekLines might modify file offset even in case of error.
//
//nolint: gocyclo
func SeekLines(r io.ReadSeeker, lines int) error {
offset, err := r.Seek(0, io.SeekEnd)
if err != nil {
return err
}
readOffset := offset - Window
if readOffset < 0 {
readOffset = 0
}
readSize := offset - readOffset
skippedLines := -1 // we need to skip (lines + 1) \n characters to find position to read from
buf := make([]byte, Window)
firstRead := true
for skippedLines < lines && readSize > 0 {
_, err = r.Seek(readOffset, io.SeekStart)
if err != nil {
return err
}
var n int
n, err = r.Read(buf[:readSize])
if err != nil {
return err
}
if int64(n) != readSize {
return fmt.Errorf("unexpected short read: %d != %d", n, readSize)
}
if firstRead && buf[n-1] != '\n' {
// last line might not have '\n'
skippedLines++
}
firstRead = false
for n > 0 && skippedLines < lines {
index := bytes.LastIndexByte(buf[:n], '\n')
if index == -1 {
break
}
skippedLines++
n = index
}
if skippedLines == lines {
readOffset += int64(n) + 1
break
}
offset = readOffset
readOffset -= Window
if readOffset < 0 {
readOffset = 0
}
readSize = offset - readOffset
}
_, err = r.Seek(readOffset, io.SeekStart)
return err
}