restic/internal/restorer/sparsewrite.go

54 lines
1.1 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) {
if !f.sparse {
return f.File.WriteAt(p, offset)
}
n = len(p)
// 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:
// All zeros, file already big enough. A previous WriteAt or
// Truncate will have produced the zeros in f.File.
default:
var n2 int
n2, err = f.File.WriteAt(p, offset)
n = skipped + n2
}
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
}