You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Bug:filex.LastLine returns a wrong line when the content after the last separator is a multiple of 1024 bytes #5787
filex.LastLine returns a wrong line when the content after the last separator is a multiple of 1024 bytes
Description
filex.LastLine can return a wrong result: part (or all) of the previous line gets merged into the returned "last line".
The bug is triggered when the number of bytes after the last separator '\n' is an exact multiple of the internal block size (bufSize = 1024) — for example when the last line is exactly 1024 bytes long and the file does not end with a newline. Fixed-length record files and machine-generated logs can hit this alignment.
Root cause
In core/filex/file.go, lastLine() reads the file backwards in 1024-byte blocks. Before scanning a block for '\n', it unconditionally trims a trailing newline:
This trim is only correct for the tail block (the first block read), where a trailing '\n' is the file's final newline.
For an interior block, a '\n' at the block's last byte is the start boundary of the last line (the next block, already read, begins exactly at the start of the last line). Trimming it destroys that boundary; the remaining block then contains no '\n', so the whole block — which belongs to the previous line — is prepended to the accumulated result:
last=append(buf, last...) // buf belongs to the previous line, wrongly merged
Precise trigger condition: (fileSize - lastSeparatorOffset - 1) % 1024 == 0, i.e. the length of the content after the last '\n' is a multiple of 1024.
Reproduction
package main
import (
"fmt""os""path/filepath""strings""github.com/zeromicro/go-zero/core/filex"
)
funcmain() {
// any previous line + '\n' + a last line of exactly 1024 bytes, no trailing newlinecontent:="previous line\n"+strings.Repeat("C", 1024)
dir, _:=os.MkdirTemp("", "repro")
deferos.RemoveAll(dir)
f:=filepath.Join(dir, "t.txt")
os.WriteFile(f, []byte(content), 0o644)
got, _:=filex.LastLine(f)
fmt.Printf("expected len=1024, got len=%d, got=%q...\n", len(got), got[:min(24, len(got))])
}
The previous line (13 bytes) is merged into the result. Adding just one byte to the last line (1025 bytes) makes the bug disappear, which confirms the alignment condition.
Suggested fix
Only trim the trailing newline on the block that reaches the end of the file:
// only the tail block can end with the file's final newline;// an interior block's trailing '\n' is the start boundary of the last lineifoffset+bufLen==info.Size() &&buf[n-1] =='\n' {
buf=buf[:n-1]
n--
} else {
buf=buf[:n]
}
With this change, an interior block ending in '\n' is handled correctly by the existing backward scan: it finds the '\n' at i = n-1 and returns buf[n:] + last, which is exactly the accumulated last (the last line starts at the next block).
Activity
fix: trim trailing newline only in tail block in filex.LastLine