base-golang/pkg/file/file.go
2024-07-23 10:23:43 +08:00

190 lines
2.9 KiB
Go

package file
import (
"bytes"
"fmt"
"io"
"os"
)
var (
buffSize = 1 << 20
)
// ReadLineFromEnd --
type ReadLineFromEnd struct {
f *os.File
fileSize int
bwr *bytes.Buffer
lineBuff []byte
swapBuff []byte
isFirst bool
}
// Exists
func IsExists(path string) (os.FileInfo, bool) {
f, err := os.Stat(path)
return f, err == nil || os.IsExist(err)
}
// NewReadLineFromEnd
func NewReadLineFromEnd(filename string) (rd *ReadLineFromEnd, err error) {
f, err := os.Open(filename)
if err != nil {
return nil, err
}
info, err := f.Stat()
if err != nil {
return nil, err
}
if info.IsDir() {
return nil, fmt.Errorf("not file")
}
fileSize := int(info.Size())
rd = &ReadLineFromEnd{
f: f,
fileSize: fileSize,
bwr: bytes.NewBuffer([]byte{}),
lineBuff: make([]byte, 0),
swapBuff: make([]byte, buffSize),
isFirst: true,
}
return rd, nil
}
// ReadLine 结尾包含'\n'
func (c *ReadLineFromEnd) ReadLine() (line []byte, err error) {
var ok bool
for {
ok, err = c.buff()
if err != nil {
return nil, err
}
if ok {
break
}
}
line, err = c.bwr.ReadBytes('\n')
if err == io.EOF && c.fileSize > 0 {
err = nil
}
return line, err
}
// Close --
func (c *ReadLineFromEnd) Close() (err error) {
return c.f.Close()
}
func (c *ReadLineFromEnd) buff() (ok bool, err error) {
if c.fileSize == 0 {
return true, nil
}
if c.bwr.Len() >= buffSize {
return true, nil
}
offset := 0
if c.fileSize > buffSize {
offset = c.fileSize - buffSize
}
_, err = c.f.Seek(int64(offset), 0)
if err != nil {
return false, err
}
n, err := c.f.Read(c.swapBuff)
if err != nil && err != io.EOF {
return false, err
}
if c.fileSize < n {
n = c.fileSize
}
if n == 0 {
return true, nil
}
for {
m := bytes.LastIndex(c.swapBuff[:n], []byte{'\n'})
if m == -1 {
break
}
if m < n-1 {
err = c.writeLine(c.swapBuff[m+1 : n])
if err != nil {
return false, err
}
ok = true
} else if m == n-1 && !c.isFirst {
err = c.writeLine(nil)
if err != nil {
return false, err
}
ok = true
}
n = m
if n == 0 {
break
}
}
if n > 0 {
reverseBytes(c.swapBuff[:n])
c.lineBuff = append(c.lineBuff, c.swapBuff[:n]...)
}
if offset == 0 {
err = c.writeLine(nil)
if err != nil {
return false, err
}
ok = true
}
c.fileSize = offset
if c.isFirst {
c.isFirst = false
}
return ok, nil
}
func (c *ReadLineFromEnd) writeLine(b []byte) (err error) {
if len(b) > 0 {
_, err = c.bwr.Write(b)
if err != nil {
return err
}
}
if len(c.lineBuff) > 0 {
reverseBytes(c.lineBuff)
_, err = c.bwr.Write(c.lineBuff)
if err != nil {
return err
}
c.lineBuff = c.lineBuff[:0]
}
_, err = c.bwr.Write([]byte{'\n'})
if err != nil {
return err
}
return nil
}
func reverseBytes(b []byte) {
n := len(b)
if n <= 1 {
return
}
for i := 0; i < n; i++ {
k := n - 1
if k != i {
b[i], b[k] = b[k], b[i]
}
n--
}
}