mirror of
https://github.com/octoleo/restic.git
synced 2024-11-10 15:21:03 +00:00
5d4568d393
This writes files by using (*os.File).Truncate, which resolves to the truncate system call on Unix. Compared to the naive loop, for _, b := range p { if b != 0 { return false } } the optimized allZero is about 10× faster: name old time/op new time/op delta AllZero-8 1.09ms ± 1% 0.09ms ± 1% -92.10% (p=0.000 n=10+10) name old speed new speed delta AllZero-8 3.84GB/s ± 1% 48.59GB/s ± 1% +1166.51% (p=0.000 n=10+10)
61 lines
1.3 KiB
Go
61 lines
1.3 KiB
Go
//go:build !windows
|
|
// +build !windows
|
|
|
|
package restorer
|
|
|
|
import "bytes"
|
|
|
|
// WriteAt writes p to f.File at offset. It tries to do a sparse write
|
|
// and updates f.size.
|
|
func (f *partialFile) WriteAt(p []byte, offset int64) (n int, err error) {
|
|
n = len(p)
|
|
end := offset + int64(n)
|
|
|
|
// Skip the longest all-zero prefix of p.
|
|
// If it's long enough, we can punch a hole in the file.
|
|
skipped := zeroPrefixLen(p)
|
|
p = p[skipped:]
|
|
offset += int64(skipped)
|
|
|
|
switch {
|
|
case len(p) == 0 && end > f.size:
|
|
// We need to do a Truncate, as WriteAt with length-0 input
|
|
// doesn't actually extend the file.
|
|
err = f.Truncate(end)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
case len(p) == 0:
|
|
// All zeros, file already big enough. A previous WriteAt or
|
|
// Truncate will have produced the zeros in f.File.
|
|
|
|
default:
|
|
n, err = f.File.WriteAt(p, offset)
|
|
}
|
|
|
|
end = offset + int64(n)
|
|
if end > f.size {
|
|
f.size = end
|
|
}
|
|
return n, err
|
|
}
|
|
|
|
// zeroPrefixLen returns the length of the longest all-zero prefix of p.
|
|
func zeroPrefixLen(p []byte) (n int) {
|
|
// First skip 1kB-sized blocks, for speed.
|
|
var zeros [1024]byte
|
|
|
|
for len(p) >= len(zeros) && bytes.Equal(p[:len(zeros)], zeros[:]) {
|
|
p = p[len(zeros):]
|
|
n += len(zeros)
|
|
}
|
|
|
|
for len(p) > 0 && p[0] == 0 {
|
|
p = p[1:]
|
|
n++
|
|
}
|
|
|
|
return n
|
|
}
|