set.go 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. package fileset
  2. import "sync"
  3. type File struct {
  4. Key Key
  5. Modified int64
  6. Flags uint32
  7. Data interface{}
  8. }
  9. type Key struct {
  10. Name string
  11. Version uint32
  12. }
  13. type fileRecord struct {
  14. Usage int
  15. File File
  16. }
  17. type bitset uint64
  18. func (a Key) newerThan(b Key) bool {
  19. return a.Version > b.Version
  20. }
  21. type Set struct {
  22. mutex sync.RWMutex
  23. files map[Key]fileRecord
  24. remoteKey [64]map[string]Key
  25. globalAvailability map[string]bitset
  26. globalKey map[string]Key
  27. }
  28. func NewSet() *Set {
  29. var m = Set{
  30. files: make(map[Key]fileRecord),
  31. globalAvailability: make(map[string]bitset),
  32. globalKey: make(map[string]Key),
  33. }
  34. return &m
  35. }
  36. func (m *Set) AddLocal(fs []File) {
  37. m.mutex.Lock()
  38. m.unlockedAddRemote(0, fs)
  39. m.mutex.Unlock()
  40. }
  41. func (m *Set) SetLocal(fs []File) {
  42. m.mutex.Lock()
  43. m.unlockedSetRemote(0, fs)
  44. m.mutex.Unlock()
  45. }
  46. func (m *Set) AddRemote(cid uint, fs []File) {
  47. if cid < 1 || cid > 63 {
  48. panic("Connection ID must be in the range 1 - 63 inclusive")
  49. }
  50. m.mutex.Lock()
  51. m.unlockedAddRemote(cid, fs)
  52. m.mutex.Unlock()
  53. }
  54. func (m *Set) SetRemote(cid uint, fs []File) {
  55. if cid < 1 || cid > 63 {
  56. panic("Connection ID must be in the range 1 - 63 inclusive")
  57. }
  58. m.mutex.Lock()
  59. m.unlockedSetRemote(cid, fs)
  60. m.mutex.Unlock()
  61. }
  62. func (m *Set) unlockedAddRemote(cid uint, fs []File) {
  63. remFiles := m.remoteKey[cid]
  64. for _, f := range fs {
  65. n := f.Key.Name
  66. if ck, ok := remFiles[n]; ok && ck == f.Key {
  67. // The remote already has exactly this file, skip it
  68. continue
  69. }
  70. remFiles[n] = f.Key
  71. // Keep the block list or increment the usage
  72. if br, ok := m.files[f.Key]; !ok {
  73. m.files[f.Key] = fileRecord{
  74. Usage: 1,
  75. File: f,
  76. }
  77. } else {
  78. br.Usage++
  79. m.files[f.Key] = br
  80. }
  81. // Update global view
  82. gk, ok := m.globalKey[n]
  83. switch {
  84. case ok && f.Key == gk:
  85. av := m.globalAvailability[n]
  86. av |= 1 << cid
  87. m.globalAvailability[n] = av
  88. case f.Key.newerThan(gk):
  89. m.globalKey[n] = f.Key
  90. m.globalAvailability[n] = 1 << cid
  91. }
  92. }
  93. }
  94. func (m *Set) unlockedSetRemote(cid uint, fs []File) {
  95. // Decrement usage for all files belonging to this remote, and remove
  96. // those that are no longer needed.
  97. for _, fk := range m.remoteKey[cid] {
  98. br, ok := m.files[fk]
  99. switch {
  100. case ok && br.Usage == 1:
  101. delete(m.files, fk)
  102. case ok && br.Usage > 1:
  103. br.Usage--
  104. m.files[fk] = br
  105. }
  106. }
  107. // Clear existing remote remoteKey
  108. m.remoteKey[cid] = make(map[string]Key)
  109. // Recalculate global based on all remaining remoteKey
  110. for n := range m.globalKey {
  111. var nk Key // newest key
  112. var na bitset // newest availability
  113. for i, rem := range m.remoteKey {
  114. if rk, ok := rem[n]; ok {
  115. switch {
  116. case rk == nk:
  117. na |= 1 << uint(i)
  118. case rk.newerThan(nk):
  119. nk = rk
  120. na = 1 << uint(i)
  121. }
  122. }
  123. }
  124. if na != 0 {
  125. // Someone had the file
  126. m.globalKey[n] = nk
  127. m.globalAvailability[n] = na
  128. } else {
  129. // Noone had the file
  130. delete(m.globalKey, n)
  131. delete(m.globalAvailability, n)
  132. }
  133. }
  134. // Add new remote remoteKey to the mix
  135. m.unlockedAddRemote(cid, fs)
  136. }
  137. func (m *Set) Need(cid uint) []File {
  138. var fs []File
  139. m.mutex.Lock()
  140. for name, gk := range m.globalKey {
  141. if gk.newerThan(m.remoteKey[cid][name]) {
  142. fs = append(fs, m.files[gk].File)
  143. }
  144. }
  145. m.mutex.Unlock()
  146. return fs
  147. }