diff --git a/diff.go b/diff.go index 1d2d951..09005a5 100644 --- a/diff.go +++ b/diff.go @@ -191,7 +191,7 @@ func Diff(old, new io.Reader, patch io.Writer) error { return err } - pbuf, err := diffBytes(obuf, nbuf) + pbuf, err := diffBytes(nil, obuf, nbuf) if err != nil { return err } @@ -200,18 +200,56 @@ func Diff(old, new io.Reader, patch io.Writer) error { return err } -func diffBytes(obuf, nbuf []byte) ([]byte, error) { +// BufSufData contains a buffer and its corresponding suffix array. +type BufSufData struct { + Buf []byte + Suf []int +} + +// ComputeSuf computes a BufSufData from reader using qsufsort algorithm. +func ComputeSuf(reader io.Reader) (*BufSufData, error) { + buf, err := ioutil.ReadAll(reader) + if err != nil { + return nil, err + } + return &BufSufData{ + Buf: buf, + Suf: qsufsort(buf), + }, nil +} + +// DiffWithSuf does the same operations as Diff with the exception that +// it optionally takes obufsuf (*BufSufData of old io.Reader) to avoid +// recreating the suffix array. +func DiffWithSuf(obufsuf *BufSufData, new io.Reader, patch io.Writer) error { + nbuf, err := ioutil.ReadAll(new) + if err != nil { + return err + } + + pbuf, err := diffBytes(obufsuf.Suf, obufsuf.Buf, nbuf) + if err != nil { + return err + } + + _, err = patch.Write(pbuf) + return err +} + +func diffBytes(I []int, obuf, nbuf []byte) ([]byte, error) { var patch seekBuffer - err := diff(obuf, nbuf, &patch) + err := diff(I, obuf, nbuf, &patch) if err != nil { return nil, err } return patch.buf, nil } -func diff(obuf, nbuf []byte, patch io.WriteSeeker) error { +func diff(I []int, obuf, nbuf []byte, patch io.WriteSeeker) error { var lenf int - I := qsufsort(obuf) + if I == nil { + I = qsufsort(obuf) + } db := make([]byte, len(nbuf)) eb := make([]byte, len(nbuf)) var dblen, eblen int diff --git a/diff_test.go b/diff_test.go index a5de018..e5f02d1 100644 --- a/diff_test.go +++ b/diff_test.go @@ -2,27 +2,30 @@ package binarydist import ( "bytes" + "io" "io/ioutil" "os" "os/exec" "testing" ) -var diffT = []struct { - old *os.File - new *os.File -}{ - { - old: mustWriteRandFile("test.old", 1e3, 1), - new: mustWriteRandFile("test.new", 1e3, 2), - }, - { - old: mustOpen("testdata/sample.old"), - new: mustOpen("testdata/sample.new"), - }, -} +func testFunc(t *testing.T, fDiff func(old, new io.Reader, patch io.Writer) error) { + t.Helper() + + diffT := []struct { + old *os.File + new *os.File + }{ + { + old: mustWriteRandFile("test.old", 1e3, 1), + new: mustWriteRandFile("test.new", 1e3, 2), + }, + { + old: mustOpen("testdata/sample.old"), + new: mustOpen("testdata/sample.new"), + }, + } -func TestDiff(t *testing.T) { for _, s := range diffT { got, err := ioutil.TempFile("/tmp", "bspatch.") if err != nil { @@ -43,7 +46,7 @@ func TestDiff(t *testing.T) { panic(err) } - err = Diff(s.old, s.new, got) + err = fDiff(s.old, s.new, got) if err != nil { t.Fatal("err", err) } @@ -65,3 +68,19 @@ func TestDiff(t *testing.T) { } } } + +func TestDiff(t *testing.T) { + testFunc(t, Diff) +} + +func TestDiffWithSuf(t *testing.T) { + fDiff := func(old, new io.Reader, patch io.Writer) error { + oldSufStruct, err := ComputeSuf(old) + if err != nil { + return err + } + + return DiffWithSuf(oldSufStruct, new, patch) + } + testFunc(t, fDiff) +}