From 0eec33f14b3d7e28c0eaa8586c2e8bb22dee034d Mon Sep 17 00:00:00 2001 From: Kevin Wan Date: Sat, 20 Jul 2024 22:44:13 +0800 Subject: [PATCH] chore: optimize file reading (#4264) --- core/filex/file.go | 27 ++++++++++----------- core/filex/file_test.go | 54 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 14 deletions(-) diff --git a/core/filex/file.go b/core/filex/file.go index 904accf9..442f6274 100644 --- a/core/filex/file.go +++ b/core/filex/file.go @@ -61,25 +61,26 @@ func lastLine(filename string, file *os.File) (string, error) { return "", err } - bf := int64(bufSize) var last []byte + bufLen := int64(bufSize) offset := info.Size() - for { - if offset < bufSize { - bf = offset + + for offset > 0 { + if offset < bufLen { + bufLen = offset offset = 0 } else { - offset -= bf + offset -= bufLen } - buf := make([]byte, bf) + buf := make([]byte, bufLen) n, err := file.ReadAt(buf, offset) if err != nil && err != io.EOF { return "", err } if n == 0 { - return "", nil + break } if buf[n-1] == '\n' { @@ -89,16 +90,14 @@ func lastLine(filename string, file *os.File) (string, error) { buf = buf[:n] } - for n--; n >= 0; n-- { - if buf[n] == '\n' { - return string(append(buf[n+1:], last...)), nil + for i := n - 1; i >= 0; i-- { + if buf[i] == '\n' { + return string(append(buf[i+1:], last...)), nil } } last = append(buf, last...) - - if offset == 0 { - return string(last), nil - } } + + return string(last), nil } diff --git a/core/filex/file_test.go b/core/filex/file_test.go index f0660d7e..76cc78d0 100644 --- a/core/filex/file_test.go +++ b/core/filex/file_test.go @@ -164,3 +164,57 @@ func TestLastLineEmptyFile(t *testing.T) { assert.Nil(t, err) assert.Equal(t, "", val) } + +func TestFirstLineExactlyBufSize(t *testing.T) { + content := make([]byte, bufSize) + for i := range content { + content[i] = 'a' + } + content[bufSize-1] = '\n' // Ensure there is a newline at the edge + + filename, err := fs.TempFilenameWithText(string(content)) + assert.Nil(t, err) + defer os.Remove(filename) + + val, err := FirstLine(filename) + assert.Nil(t, err) + assert.Equal(t, string(content[:bufSize-1]), val) +} + +func TestLastLineExactlyBufSize(t *testing.T) { + content := make([]byte, bufSize) + for i := range content { + content[i] = 'a' + } + content[bufSize-1] = '\n' // Ensure there is a newline at the edge + + filename, err := fs.TempFilenameWithText(string(content)) + assert.Nil(t, err) + defer os.Remove(filename) + + val, err := LastLine(filename) + assert.Nil(t, err) + assert.Equal(t, string(content[:bufSize-1]), val) +} + +func TestFirstLineLargeFile(t *testing.T) { + content := text + text + text + "\n" + "extra" + filename, err := fs.TempFilenameWithText(content) + assert.Nil(t, err) + defer os.Remove(filename) + + val, err := FirstLine(filename) + assert.Nil(t, err) + assert.Equal(t, "first line", val) +} + +func TestLastLineLargeFile(t *testing.T) { + content := text + text + text + "\n" + "extra" + filename, err := fs.TempFilenameWithText(content) + assert.Nil(t, err) + defer os.Remove(filename) + + val, err := LastLine(filename) + assert.Nil(t, err) + assert.Equal(t, "extra", val) +}