forked from TrueCloudLab/restic
restorer: move zeroPrefixLen to restic package
This commit is contained in:
parent
a5ebd5de4b
commit
34fe1362da
4 changed files with 77 additions and 48 deletions
21
internal/restic/zeroprefix.go
Normal file
21
internal/restic/zeroprefix.go
Normal file
|
@ -0,0 +1,21 @@
|
|||
package restic
|
||||
|
||||
import "bytes"
|
||||
|
||||
// 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
|
||||
}
|
52
internal/restic/zeroprefix_test.go
Normal file
52
internal/restic/zeroprefix_test.go
Normal file
|
@ -0,0 +1,52 @@
|
|||
package restic_test
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"testing"
|
||||
|
||||
"github.com/restic/restic/internal/restic"
|
||||
"github.com/restic/restic/internal/test"
|
||||
)
|
||||
|
||||
func TestZeroPrefixLen(t *testing.T) {
|
||||
var buf [2048]byte
|
||||
|
||||
// test zero prefixes of various lengths
|
||||
for i := len(buf) - 1; i >= 0; i-- {
|
||||
buf[i] = 42
|
||||
skipped := restic.ZeroPrefixLen(buf[:])
|
||||
test.Equals(t, i, skipped)
|
||||
}
|
||||
// test buffers of various sizes
|
||||
for i := 0; i < len(buf); i++ {
|
||||
skipped := restic.ZeroPrefixLen(buf[i:])
|
||||
test.Equals(t, 0, skipped)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkZeroPrefixLen(b *testing.B) {
|
||||
var (
|
||||
buf [4<<20 + 37]byte
|
||||
r = rand.New(rand.NewSource(0x618732))
|
||||
sumSkipped int64
|
||||
)
|
||||
|
||||
b.ReportAllocs()
|
||||
b.SetBytes(int64(len(buf)))
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
j := r.Intn(len(buf))
|
||||
buf[j] = 0xff
|
||||
|
||||
skipped := restic.ZeroPrefixLen(buf[:])
|
||||
sumSkipped += int64(skipped)
|
||||
|
||||
buf[j] = 0
|
||||
}
|
||||
|
||||
// The closer this is to .5, the better. If it's far off, give the
|
||||
// benchmark more time to run with -benchtime.
|
||||
b.Logf("average number of zeros skipped: %.3f",
|
||||
float64(sumSkipped)/(float64(b.N*len(buf))))
|
||||
}
|
|
@ -8,7 +8,6 @@ import (
|
|||
"context"
|
||||
"io/ioutil"
|
||||
"math"
|
||||
"math/rand"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
|
@ -123,30 +122,3 @@ func TestRestorerSparseFiles(t *testing.T) {
|
|||
t.Logf("wrote %d zeros as %d blocks, %.1f%% sparse",
|
||||
len(zeros), st.Blocks, 100*sparsity)
|
||||
}
|
||||
|
||||
func BenchmarkZeroPrefixLen(b *testing.B) {
|
||||
var (
|
||||
buf [4<<20 + 37]byte
|
||||
r = rand.New(rand.NewSource(0x618732))
|
||||
sumSkipped int64
|
||||
)
|
||||
|
||||
b.ReportAllocs()
|
||||
b.SetBytes(int64(len(buf)))
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
j := r.Intn(len(buf))
|
||||
buf[j] = 0xff
|
||||
|
||||
skipped := zeroPrefixLen(buf[:])
|
||||
sumSkipped += int64(skipped)
|
||||
|
||||
buf[j] = 0
|
||||
}
|
||||
|
||||
// The closer this is to .5, the better. If it's far off, give the
|
||||
// benchmark more time to run with -benchtime.
|
||||
b.Logf("average number of zeros skipped: %.3f",
|
||||
float64(sumSkipped)/(float64(b.N*len(buf))))
|
||||
}
|
||||
|
|
|
@ -3,7 +3,9 @@
|
|||
|
||||
package restorer
|
||||
|
||||
import "bytes"
|
||||
import (
|
||||
"github.com/restic/restic/internal/restic"
|
||||
)
|
||||
|
||||
// WriteAt writes p to f.File at offset. It tries to do a sparse write
|
||||
// and updates f.size.
|
||||
|
@ -16,7 +18,7 @@ func (f *partialFile) WriteAt(p []byte, offset int64) (n int, err error) {
|
|||
|
||||
// Skip the longest all-zero prefix of p.
|
||||
// If it's long enough, we can punch a hole in the file.
|
||||
skipped := zeroPrefixLen(p)
|
||||
skipped := restic.ZeroPrefixLen(p)
|
||||
p = p[skipped:]
|
||||
offset += int64(skipped)
|
||||
|
||||
|
@ -33,21 +35,3 @@ func (f *partialFile) WriteAt(p []byte, offset int64) (n int, err error) {
|
|||
|
||||
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
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue