| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173 |
- 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
- }
|