Snapshot difference algorithm.

This commit is contained in:
Andrey Smirnov
2014-01-13 20:27:14 +04:00
parent e70517b9ca
commit 49c8c8bdc0
2 changed files with 169 additions and 0 deletions

97
debian/list.go vendored
View File

@@ -376,3 +376,100 @@ func (l *PackageRefList) ForEach(handler func([]byte) error) error {
}
return err
}
// PackageDiff is a difference between two packages in a list.
//
// If left & right are present, difference is in package version
// If left is nil, package is present only in right
// If right is nil, package is present only in left
type PackageDiff struct {
Left, Right *Package
}
// PackageDiffs is a list of PackageDiff records
type PackageDiffs []PackageDiff
// Diff calculates difference between two reflists
func (l *PackageRefList) Diff(r *PackageRefList, packageCollection *PackageCollection) (result PackageDiffs, err error) {
result = make(PackageDiffs, 0, 128)
// pointer to left and right reflists
il, ir := 0, 0
// length of reflists
ll, lr := l.Len(), r.Len()
// cached loaded packages on the left & right
pl, pr := (*Package)(nil), (*Package)(nil)
// until we reached end of both lists
for il < ll || ir < lr {
// if we've exhausted left list, pull the rest from the right
if il == ll {
pr, err = packageCollection.ByKey(r.Refs[ir])
if err != nil {
return nil, err
}
result = append(result, PackageDiff{Left: nil, Right: pr})
ir++
continue
}
// if we've exhausted right list, pull the rest from the left
if ir == lr {
pl, err = packageCollection.ByKey(l.Refs[il])
if err != nil {
return nil, err
}
result = append(result, PackageDiff{Left: pl, Right: nil})
il++
continue
}
// refs on both sides are present, load them
rl, rr := l.Refs[il], r.Refs[ir]
// compare refs
rel := bytes.Compare(rl, rr)
if rel == 0 {
// refs are identical, so are packages, advance pointer
il++
ir++
pl, pr = nil, nil
} else {
// load pl & pr if they haven't been loaded before
if pl == nil {
pl, err = packageCollection.ByKey(rl)
if err != nil {
return nil, err
}
}
if pr == nil {
pr, err = packageCollection.ByKey(rr)
if err != nil {
return nil, err
}
}
// is pl & pr the same package, but different version?
if pl.Name == pr.Name && pl.Architecture == pr.Architecture {
result = append(result, PackageDiff{Left: pl, Right: pr})
il++
ir++
pl, pr = nil, nil
} else {
// otherwise pl or pr is missing on one of the sides
if rel < 0 {
result = append(result, PackageDiff{Left: pl, Right: nil})
il++
pl = nil
} else {
result = append(result, PackageDiff{Left: nil, Right: pr})
ir++
pr = nil
}
}
}
}
return
}

72
debian/list_test.go vendored
View File

@@ -322,3 +322,75 @@ func (s *PackageListSuite) TestPackageRefListForeach(c *C) {
c.Check(err, Equals, e)
}
func (s *PackageListSuite) TestDiff(c *C) {
db, _ := database.OpenDB(c.MkDir())
coll := NewPackageCollection(db)
packages := []*Package{
&Package{Name: "lib", Version: "1.0", Architecture: "i386"}, //0
&Package{Name: "dpkg", Version: "1.7", Architecture: "i386"}, //1
&Package{Name: "data", Version: "1.1~bp1", Architecture: "all"}, //2
&Package{Name: "app", Version: "1.1~bp1", Architecture: "i386"}, //3
&Package{Name: "app", Version: "1.1~bp2", Architecture: "i386"}, //4
&Package{Name: "app", Version: "1.1~bp2", Architecture: "amd64"}, //5
&Package{Name: "xyz", Version: "3.0", Architecture: "sparc"}, //6
}
for _, p := range packages {
coll.Update(p)
}
listA := NewPackageList()
listA.Add(packages[0])
listA.Add(packages[1])
listA.Add(packages[2])
listA.Add(packages[3])
listA.Add(packages[6])
listB := NewPackageList()
listB.Add(packages[0])
listB.Add(packages[2])
listB.Add(packages[4])
listB.Add(packages[5])
reflistA := NewPackageRefListFromPackageList(listA)
reflistB := NewPackageRefListFromPackageList(listB)
diffAA, err := reflistA.Diff(reflistA, coll)
c.Check(err, IsNil)
c.Check(diffAA, HasLen, 0)
diffAB, err := reflistA.Diff(reflistB, coll)
c.Check(err, IsNil)
c.Check(diffAB, HasLen, 4)
c.Check(diffAB[0].Left, IsNil)
c.Check(diffAB[0].Right.String(), Equals, "app-1.1~bp2_amd64")
c.Check(diffAB[1].Left.String(), Equals, "app-1.1~bp1_i386")
c.Check(diffAB[1].Right.String(), Equals, "app-1.1~bp2_i386")
c.Check(diffAB[2].Left.String(), Equals, "dpkg-1.7_i386")
c.Check(diffAB[2].Right, IsNil)
c.Check(diffAB[3].Left.String(), Equals, "xyz-3.0_sparc")
c.Check(diffAB[3].Right, IsNil)
diffBA, err := reflistB.Diff(reflistA, coll)
c.Check(err, IsNil)
c.Check(diffBA, HasLen, 4)
c.Check(diffBA[0].Right, IsNil)
c.Check(diffBA[0].Left.String(), Equals, "app-1.1~bp2_amd64")
c.Check(diffBA[1].Right.String(), Equals, "app-1.1~bp1_i386")
c.Check(diffBA[1].Left.String(), Equals, "app-1.1~bp2_i386")
c.Check(diffBA[2].Right.String(), Equals, "dpkg-1.7_i386")
c.Check(diffBA[2].Left, IsNil)
c.Check(diffBA[3].Right.String(), Equals, "xyz-3.0_sparc")
c.Check(diffBA[3].Left, IsNil)
}