Skip to content

Instantly share code, notes, and snippets.

@scorredoira
Created October 17, 2016 08:48
Show Gist options
  • Save scorredoira/cdcf062955be832f88673d792bde5933 to your computer and use it in GitHub Desktop.
Save scorredoira/cdcf062955be832f88673d792bde5933 to your computer and use it in GitHub Desktop.
package files
import (
"bytes"
"os"
)
func NewBackScanner(f *os.File) (*BackScanner, error) {
s, err := f.Stat()
if err != nil {
return nil, err
}
size := s.Size()
return &BackScanner{file: f, size: size, index: size}, nil
}
// BackScanner reads lines from the end of a file to de beginning.
type BackScanner struct {
file *os.File
size int64
index int64
buf []byte
err error
}
func (s *BackScanner) Scan() bool {
if s.index == -1 {
return false
}
i, err := s.prev()
if err != nil {
s.err = err
return false
}
last := s.index
s.index = i
if i == -1 {
i = 0
}
l := last - i
if l > 0 {
buf := make([]byte, l)
if _, err := s.file.ReadAt(buf, i); err != nil {
s.err = err
return false
}
if i > 0 {
buf = buf[1:] // trim the linebreak
}
s.buf = buf
}
return true
}
func (s *BackScanner) Error() error {
return s.err
}
func (s *BackScanner) Bytes() []byte {
return s.buf
}
func (s *BackScanner) Text() string {
return string(s.buf)
}
func (s *BackScanner) Close() {
s.file.Close()
}
func (s *BackScanner) prev() (int64, error) {
// this block size arbitrary value but most lines would be < 200B.
block := int64(400)
start := s.index
// check if the file is smaller than a block
if block > start {
block = start
}
for {
// go backwards one block to start reading
start -= block
if start < 0 {
// if we are before the beginning of the file just read the remaining part.
block += start
start = 0
if block <= 0 {
// nothing left to read
return -1, nil
}
}
// read the current blok
buf := make([]byte, block)
if _, err := s.file.ReadAt(buf, start); err != nil {
return 0, err
}
// return the last linebreak
if i := bytes.LastIndexByte(buf, '\n'); i != -1 {
return start + int64(i), nil
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment