mirror of
https://github.com/octoleo/syncthing.git
synced 2024-11-09 23:00:58 +00:00
174 lines
3.3 KiB
Go
174 lines
3.3 KiB
Go
package fileset
|
|
|
|
import "sync"
|
|
|
|
type File struct {
|
|
Key Key
|
|
Modified int64
|
|
Flags uint32
|
|
Data interface{}
|
|
}
|
|
|
|
type Key struct {
|
|
Name string
|
|
Version uint32
|
|
}
|
|
|
|
type fileRecord struct {
|
|
Usage int
|
|
File File
|
|
}
|
|
|
|
type bitset uint64
|
|
|
|
func (a Key) newerThan(b Key) bool {
|
|
return a.Version > b.Version
|
|
}
|
|
|
|
type Set struct {
|
|
mutex sync.RWMutex
|
|
files map[Key]fileRecord
|
|
remoteKey [64]map[string]Key
|
|
globalAvailability map[string]bitset
|
|
globalKey map[string]Key
|
|
}
|
|
|
|
func NewSet() *Set {
|
|
var m = Set{
|
|
files: make(map[Key]fileRecord),
|
|
globalAvailability: make(map[string]bitset),
|
|
globalKey: make(map[string]Key),
|
|
}
|
|
return &m
|
|
}
|
|
|
|
func (m *Set) AddLocal(fs []File) {
|
|
m.mutex.Lock()
|
|
m.unlockedAddRemote(0, fs)
|
|
m.mutex.Unlock()
|
|
}
|
|
|
|
func (m *Set) SetLocal(fs []File) {
|
|
m.mutex.Lock()
|
|
m.unlockedSetRemote(0, fs)
|
|
m.mutex.Unlock()
|
|
}
|
|
|
|
func (m *Set) AddRemote(cid uint, fs []File) {
|
|
if cid < 1 || cid > 63 {
|
|
panic("Connection ID must be in the range 1 - 63 inclusive")
|
|
}
|
|
m.mutex.Lock()
|
|
m.unlockedAddRemote(cid, fs)
|
|
m.mutex.Unlock()
|
|
}
|
|
|
|
func (m *Set) SetRemote(cid uint, fs []File) {
|
|
if cid < 1 || cid > 63 {
|
|
panic("Connection ID must be in the range 1 - 63 inclusive")
|
|
}
|
|
m.mutex.Lock()
|
|
m.unlockedSetRemote(cid, fs)
|
|
m.mutex.Unlock()
|
|
}
|
|
|
|
func (m *Set) unlockedAddRemote(cid uint, fs []File) {
|
|
remFiles := m.remoteKey[cid]
|
|
for _, f := range fs {
|
|
n := f.Key.Name
|
|
|
|
if ck, ok := remFiles[n]; ok && ck == f.Key {
|
|
// The remote already has exactly this file, skip it
|
|
continue
|
|
}
|
|
|
|
remFiles[n] = f.Key
|
|
|
|
// Keep the block list or increment the usage
|
|
if br, ok := m.files[f.Key]; !ok {
|
|
m.files[f.Key] = fileRecord{
|
|
Usage: 1,
|
|
File: f,
|
|
}
|
|
} else {
|
|
br.Usage++
|
|
m.files[f.Key] = br
|
|
}
|
|
|
|
// Update global view
|
|
gk, ok := m.globalKey[n]
|
|
switch {
|
|
case ok && f.Key == gk:
|
|
av := m.globalAvailability[n]
|
|
av |= 1 << cid
|
|
m.globalAvailability[n] = av
|
|
case f.Key.newerThan(gk):
|
|
m.globalKey[n] = f.Key
|
|
m.globalAvailability[n] = 1 << cid
|
|
}
|
|
}
|
|
}
|
|
|
|
func (m *Set) unlockedSetRemote(cid uint, fs []File) {
|
|
// Decrement usage for all files belonging to this remote, and remove
|
|
// those that are no longer needed.
|
|
for _, fk := range m.remoteKey[cid] {
|
|
br, ok := m.files[fk]
|
|
switch {
|
|
case ok && br.Usage == 1:
|
|
delete(m.files, fk)
|
|
case ok && br.Usage > 1:
|
|
br.Usage--
|
|
m.files[fk] = br
|
|
}
|
|
}
|
|
|
|
// Clear existing remote remoteKey
|
|
m.remoteKey[cid] = make(map[string]Key)
|
|
|
|
// Recalculate global based on all remaining remoteKey
|
|
for n := range m.globalKey {
|
|
var nk Key // newest key
|
|
var na bitset // newest availability
|
|
|
|
for i, rem := range m.remoteKey {
|
|
if rk, ok := rem[n]; ok {
|
|
switch {
|
|
case rk == nk:
|
|
na |= 1 << uint(i)
|
|
case rk.newerThan(nk):
|
|
nk = rk
|
|
na = 1 << uint(i)
|
|
}
|
|
}
|
|
}
|
|
|
|
if na != 0 {
|
|
// Someone had the file
|
|
m.globalKey[n] = nk
|
|
m.globalAvailability[n] = na
|
|
} else {
|
|
// Noone had the file
|
|
delete(m.globalKey, n)
|
|
delete(m.globalAvailability, n)
|
|
}
|
|
}
|
|
|
|
// Add new remote remoteKey to the mix
|
|
m.unlockedAddRemote(cid, fs)
|
|
}
|
|
|
|
func (m *Set) Need(cid uint) []File {
|
|
var fs []File
|
|
m.mutex.Lock()
|
|
|
|
for name, gk := range m.globalKey {
|
|
if gk.newerThan(m.remoteKey[cid][name]) {
|
|
fs = append(fs, m.files[gk].File)
|
|
}
|
|
}
|
|
|
|
m.mutex.Unlock()
|
|
return fs
|
|
}
|