mirror of
https://github.com/octoleo/syncthing.git
synced 2024-11-18 19:15:19 +00:00
674fca3868
Avoid havoc when discovering locally-deleted-upgraded files during repair / need calculation... Co-authored-by: Simon Frei <freisim93@gmail.com>
232 lines
5.6 KiB
Go
232 lines
5.6 KiB
Go
// Copyright (C) 2015 The Protocol Authors.
|
|
|
|
package protocol
|
|
|
|
import "time"
|
|
|
|
// The Vector type represents a version vector. The zero value is a usable
|
|
// version vector. The vector has slice semantics and some operations on it
|
|
// are "append-like" in that they may return the same vector modified, or v
|
|
// new allocated Vector with the modified contents.
|
|
|
|
// Counter represents a single counter in the version vector.
|
|
|
|
// Update returns a Vector with the index for the specific ID incremented by
|
|
// one. If it is possible, the vector v is updated and returned. If it is not,
|
|
// a copy will be created, updated and returned.
|
|
func (v Vector) Update(id ShortID) Vector {
|
|
now := uint64(time.Now().Unix())
|
|
return v.updateWithNow(id, now)
|
|
}
|
|
|
|
func (v Vector) updateWithNow(id ShortID, now uint64) Vector {
|
|
for i := range v.Counters {
|
|
if v.Counters[i].ID == id {
|
|
// Update an existing index
|
|
v.Counters[i].Value = max(v.Counters[i].Value+1, now)
|
|
return v
|
|
} else if v.Counters[i].ID > id {
|
|
// Insert a new index
|
|
nv := make([]Counter, len(v.Counters)+1)
|
|
copy(nv, v.Counters[:i])
|
|
nv[i].ID = id
|
|
nv[i].Value = max(1, now)
|
|
copy(nv[i+1:], v.Counters[i:])
|
|
return Vector{Counters: nv}
|
|
}
|
|
}
|
|
|
|
// Append a new index
|
|
return Vector{Counters: append(v.Counters, Counter{
|
|
ID: id,
|
|
Value: max(1, now),
|
|
})}
|
|
}
|
|
|
|
func max(a, b uint64) uint64 {
|
|
if a > b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|
|
|
|
// Merge returns the vector containing the maximum indexes from v and b. If it
|
|
// is possible, the vector v is updated and returned. If it is not, a copy
|
|
// will be created, updated and returned.
|
|
func (v Vector) Merge(b Vector) Vector {
|
|
var vi, bi int
|
|
for bi < len(b.Counters) {
|
|
if vi == len(v.Counters) {
|
|
// We've reach the end of v, all that remains are appends
|
|
return Vector{Counters: append(v.Counters, b.Counters[bi:]...)}
|
|
}
|
|
|
|
if v.Counters[vi].ID > b.Counters[bi].ID {
|
|
// The index from b should be inserted here
|
|
n := make([]Counter, len(v.Counters)+1)
|
|
copy(n, v.Counters[:vi])
|
|
n[vi] = b.Counters[bi]
|
|
copy(n[vi+1:], v.Counters[vi:])
|
|
v.Counters = n
|
|
}
|
|
|
|
if v.Counters[vi].ID == b.Counters[bi].ID {
|
|
if val := b.Counters[bi].Value; val > v.Counters[vi].Value {
|
|
v.Counters[vi].Value = val
|
|
}
|
|
}
|
|
|
|
if bi < len(b.Counters) && v.Counters[vi].ID == b.Counters[bi].ID {
|
|
bi++
|
|
}
|
|
vi++
|
|
}
|
|
|
|
return v
|
|
}
|
|
|
|
// Copy returns an identical vector that is not shared with v.
|
|
func (v Vector) Copy() Vector {
|
|
nv := make([]Counter, len(v.Counters))
|
|
copy(nv, v.Counters)
|
|
return Vector{Counters: nv}
|
|
}
|
|
|
|
// Equal returns true when the two vectors are equivalent.
|
|
func (v Vector) Equal(b Vector) bool {
|
|
return v.Compare(b) == Equal
|
|
}
|
|
|
|
// LesserEqual returns true when the two vectors are equivalent or v is Lesser
|
|
// than b.
|
|
func (v Vector) LesserEqual(b Vector) bool {
|
|
comp := v.Compare(b)
|
|
return comp == Lesser || comp == Equal
|
|
}
|
|
|
|
// GreaterEqual returns true when the two vectors are equivalent or v is Greater
|
|
// than b.
|
|
func (v Vector) GreaterEqual(b Vector) bool {
|
|
comp := v.Compare(b)
|
|
return comp == Greater || comp == Equal
|
|
}
|
|
|
|
// Concurrent returns true when the two vectors are concurrent.
|
|
func (v Vector) Concurrent(b Vector) bool {
|
|
comp := v.Compare(b)
|
|
return comp == ConcurrentGreater || comp == ConcurrentLesser
|
|
}
|
|
|
|
// Counter returns the current value of the given counter ID.
|
|
func (v Vector) Counter(id ShortID) uint64 {
|
|
for _, c := range v.Counters {
|
|
if c.ID == id {
|
|
return c.Value
|
|
}
|
|
}
|
|
return 0
|
|
}
|
|
|
|
// IsEmpty returns true when there are no counters.
|
|
func (v Vector) IsEmpty() bool {
|
|
return len(v.Counters) == 0
|
|
}
|
|
|
|
// DropOthers removes all counters, keeping only the one with given id. If there
|
|
// is no such counter, an empty Vector is returned.
|
|
func (v Vector) DropOthers(id ShortID) Vector {
|
|
for i, c := range v.Counters {
|
|
if c.ID == id {
|
|
v.Counters = v.Counters[i : i+1]
|
|
return v
|
|
}
|
|
}
|
|
return Vector{}
|
|
}
|
|
|
|
// Ordering represents the relationship between two Vectors.
|
|
type Ordering int
|
|
|
|
const (
|
|
Equal Ordering = iota
|
|
Greater
|
|
Lesser
|
|
ConcurrentLesser
|
|
ConcurrentGreater
|
|
)
|
|
|
|
// There's really no such thing as "concurrent lesser" and "concurrent
|
|
// greater" in version vectors, just "concurrent". But it's useful to be able
|
|
// to get a strict ordering between versions for stable sorts and so on, so we
|
|
// return both variants. The convenience method Concurrent() can be used to
|
|
// check for either case.
|
|
|
|
// Compare returns the Ordering that describes a's relation to b.
|
|
func (v Vector) Compare(b Vector) Ordering {
|
|
var ai, bi int // index into a and b
|
|
var av, bv Counter // value at current index
|
|
|
|
result := Equal
|
|
|
|
for ai < len(v.Counters) || bi < len(b.Counters) {
|
|
var aMissing, bMissing bool
|
|
|
|
if ai < len(v.Counters) {
|
|
av = v.Counters[ai]
|
|
} else {
|
|
av = Counter{}
|
|
aMissing = true
|
|
}
|
|
|
|
if bi < len(b.Counters) {
|
|
bv = b.Counters[bi]
|
|
} else {
|
|
bv = Counter{}
|
|
bMissing = true
|
|
}
|
|
|
|
switch {
|
|
case av.ID == bv.ID:
|
|
// We have a counter value for each side
|
|
if av.Value > bv.Value {
|
|
if result == Lesser {
|
|
return ConcurrentLesser
|
|
}
|
|
result = Greater
|
|
} else if av.Value < bv.Value {
|
|
if result == Greater {
|
|
return ConcurrentGreater
|
|
}
|
|
result = Lesser
|
|
}
|
|
|
|
case !aMissing && av.ID < bv.ID || bMissing:
|
|
// Value is missing on the b side
|
|
if av.Value > 0 {
|
|
if result == Lesser {
|
|
return ConcurrentLesser
|
|
}
|
|
result = Greater
|
|
}
|
|
|
|
case !bMissing && bv.ID < av.ID || aMissing:
|
|
// Value is missing on the a side
|
|
if bv.Value > 0 {
|
|
if result == Greater {
|
|
return ConcurrentGreater
|
|
}
|
|
result = Lesser
|
|
}
|
|
}
|
|
|
|
if ai < len(v.Counters) && (av.ID <= bv.ID || bMissing) {
|
|
ai++
|
|
}
|
|
if bi < len(b.Counters) && (bv.ID <= av.ID || aMissing) {
|
|
bi++
|
|
}
|
|
}
|
|
|
|
return result
|
|
}
|