mirror of
https://github.com/octoleo/restic.git
synced 2024-12-22 10:58:55 +00:00
restorer: move zeroPrefixLen to restic package
This commit is contained in:
parent
a5ebd5de4b
commit
34fe1362da
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"
|
"context"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"math"
|
"math"
|
||||||
"math/rand"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"syscall"
|
"syscall"
|
||||||
@ -123,30 +122,3 @@ func TestRestorerSparseFiles(t *testing.T) {
|
|||||||
t.Logf("wrote %d zeros as %d blocks, %.1f%% sparse",
|
t.Logf("wrote %d zeros as %d blocks, %.1f%% sparse",
|
||||||
len(zeros), st.Blocks, 100*sparsity)
|
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
|
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
|
// WriteAt writes p to f.File at offset. It tries to do a sparse write
|
||||||
// and updates f.size.
|
// 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.
|
// Skip the longest all-zero prefix of p.
|
||||||
// If it's long enough, we can punch a hole in the file.
|
// If it's long enough, we can punch a hole in the file.
|
||||||
skipped := zeroPrefixLen(p)
|
skipped := restic.ZeroPrefixLen(p)
|
||||||
p = p[skipped:]
|
p = p[skipped:]
|
||||||
offset += int64(skipped)
|
offset += int64(skipped)
|
||||||
|
|
||||||
@ -33,21 +35,3 @@ func (f *partialFile) WriteAt(p []byte, offset int64) (n int, err error) {
|
|||||||
|
|
||||||
return n, err
|
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 New Issue
Block a user