| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304 |
- // Package files provides a set type to track local/remote files with newness checks.
- package files
- import (
- "sync"
- "github.com/calmh/syncthing/cid"
- "github.com/calmh/syncthing/lamport"
- "github.com/calmh/syncthing/protocol"
- "github.com/calmh/syncthing/scanner"
- )
- type fileRecord struct {
- File scanner.File
- Usage int
- Global bool
- }
- type bitset uint64
- type Set struct {
- sync.Mutex
- files map[key]fileRecord
- remoteKey [64]map[string]key
- changes [64]uint64
- 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) Replace(id uint, fs []scanner.File) {
- if debug {
- l.Debugf("Replace(%d, [%d])", id, len(fs))
- }
- if id > 63 {
- panic("Connection ID must be in the range 0 - 63 inclusive")
- }
- m.Lock()
- if len(fs) == 0 || !m.equals(id, fs) {
- m.changes[id]++
- m.replace(id, fs)
- }
- m.Unlock()
- }
- func (m *Set) ReplaceWithDelete(id uint, fs []scanner.File) {
- if debug {
- l.Debugf("ReplaceWithDelete(%d, [%d])", id, len(fs))
- }
- if id > 63 {
- panic("Connection ID must be in the range 0 - 63 inclusive")
- }
- m.Lock()
- if len(fs) == 0 || !m.equals(id, fs) {
- m.changes[id]++
- var nf = make(map[string]key, len(fs))
- for _, f := range fs {
- nf[f.Name] = keyFor(f)
- }
- // For previously existing files not in the list, add them to the list
- // with the relevant delete flags etc set. Previously existing files
- // with the delete bit already set are not modified.
- for _, ck := range m.remoteKey[cid.LocalID] {
- if _, ok := nf[ck.Name]; !ok {
- cf := m.files[ck].File
- if !protocol.IsDeleted(cf.Flags) {
- cf.Flags |= protocol.FlagDeleted
- cf.Blocks = nil
- cf.Size = 0
- cf.Version = lamport.Default.Tick(cf.Version)
- }
- fs = append(fs, cf)
- if debug {
- l.Debugln("deleted:", ck.Name)
- }
- }
- }
- m.replace(id, fs)
- }
- m.Unlock()
- }
- func (m *Set) Update(id uint, fs []scanner.File) {
- if debug {
- l.Debugf("Update(%d, [%d])", id, len(fs))
- }
- m.Lock()
- m.update(id, fs)
- m.changes[id]++
- m.Unlock()
- }
- func (m *Set) Need(id uint) []scanner.File {
- if debug {
- l.Debugf("Need(%d)", id)
- }
- m.Lock()
- var fs = make([]scanner.File, 0, len(m.globalKey)/2) // Just a guess, but avoids too many reallocations
- rkID := m.remoteKey[id]
- for gk, gf := range m.files {
- if !gf.Global || gf.File.Suppressed {
- continue
- }
- if gk.newerThan(rkID[gk.Name]) {
- fs = append(fs, gf.File)
- }
- }
- m.Unlock()
- return fs
- }
- func (m *Set) Have(id uint) []scanner.File {
- if debug {
- l.Debugf("Have(%d)", id)
- }
- var fs = make([]scanner.File, 0, len(m.remoteKey[id]))
- m.Lock()
- for _, rk := range m.remoteKey[id] {
- fs = append(fs, m.files[rk].File)
- }
- m.Unlock()
- return fs
- }
- func (m *Set) Global() []scanner.File {
- if debug {
- l.Debugf("Global()")
- }
- m.Lock()
- var fs = make([]scanner.File, 0, len(m.globalKey))
- for _, file := range m.files {
- if file.Global {
- fs = append(fs, file.File)
- }
- }
- m.Unlock()
- return fs
- }
- func (m *Set) Get(id uint, file string) scanner.File {
- m.Lock()
- defer m.Unlock()
- if debug {
- l.Debugf("Get(%d, %q)", id, file)
- }
- return m.files[m.remoteKey[id][file]].File
- }
- func (m *Set) GetGlobal(file string) scanner.File {
- m.Lock()
- defer m.Unlock()
- if debug {
- l.Debugf("GetGlobal(%q)", file)
- }
- return m.files[m.globalKey[file]].File
- }
- func (m *Set) Availability(name string) bitset {
- m.Lock()
- defer m.Unlock()
- av := m.globalAvailability[name]
- if debug {
- l.Debugf("Availability(%q) = %0x", name, av)
- }
- return av
- }
- func (m *Set) Changes(id uint) uint64 {
- m.Lock()
- defer m.Unlock()
- if debug {
- l.Debugf("Changes(%d)", id)
- }
- return m.changes[id]
- }
- func (m *Set) equals(id uint, fs []scanner.File) bool {
- curWithoutDeleted := make(map[string]key)
- for _, k := range m.remoteKey[id] {
- f := m.files[k].File
- if !protocol.IsDeleted(f.Flags) {
- curWithoutDeleted[f.Name] = k
- }
- }
- if len(curWithoutDeleted) != len(fs) {
- return false
- }
- for _, f := range fs {
- if curWithoutDeleted[f.Name] != keyFor(f) {
- return false
- }
- }
- return true
- }
- func (m *Set) update(cid uint, fs []scanner.File) {
- remFiles := m.remoteKey[cid]
- for _, f := range fs {
- n := f.Name
- fk := keyFor(f)
- if ck, ok := remFiles[n]; ok && ck == fk {
- // The remote already has exactly this file, skip it
- continue
- }
- remFiles[n] = fk
- // Keep the block list or increment the usage
- if br, ok := m.files[fk]; !ok {
- m.files[fk] = fileRecord{
- Usage: 1,
- File: f,
- }
- } else {
- br.Usage++
- m.files[fk] = br
- }
- // Update global view
- gk, ok := m.globalKey[n]
- switch {
- case ok && fk == gk:
- av := m.globalAvailability[n]
- av |= 1 << cid
- m.globalAvailability[n] = av
- case fk.newerThan(gk):
- if ok {
- f := m.files[gk]
- f.Global = false
- m.files[gk] = f
- }
- f := m.files[fk]
- f.Global = true
- m.files[fk] = f
- m.globalKey[n] = fk
- m.globalAvailability[n] = 1 << cid
- }
- }
- }
- func (m *Set) replace(cid uint, fs []scanner.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.update(cid, fs)
- }
|