devicedownloadstate.go 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. // Copyright (C) 2015 The Syncthing Authors.
  2. //
  3. // This Source Code Form is subject to the terms of the Mozilla Public
  4. // License, v. 2.0. If a copy of the MPL was not distributed with this file,
  5. // You can obtain one at https://mozilla.org/MPL/2.0/.
  6. package model
  7. import (
  8. "github.com/syncthing/syncthing/lib/protocol"
  9. "github.com/syncthing/syncthing/lib/sync"
  10. )
  11. // deviceFolderFileDownloadState holds current download state of a file that
  12. // a remote device has advertised. blockIndexes represends indexes within
  13. // FileInfo.Blocks that the remote device already has, and version represents
  14. // the version of the file that the remote device is downloading.
  15. type deviceFolderFileDownloadState struct {
  16. blockIndexes []int
  17. version protocol.Vector
  18. blockSize int
  19. }
  20. // deviceFolderDownloadState holds current download state of all files that
  21. // a remote device is currently downloading in a specific folder.
  22. type deviceFolderDownloadState struct {
  23. mut sync.RWMutex
  24. files map[string]deviceFolderFileDownloadState
  25. }
  26. // Has returns whether a block at that specific index, and that specific version of the file
  27. // is currently available on the remote device for pulling from a temporary file.
  28. func (p *deviceFolderDownloadState) Has(file string, version protocol.Vector, index int) bool {
  29. p.mut.RLock()
  30. defer p.mut.RUnlock()
  31. local, ok := p.files[file]
  32. if !ok || !local.version.Equal(version) {
  33. return false
  34. }
  35. for _, existingIndex := range local.blockIndexes {
  36. if existingIndex == index {
  37. return true
  38. }
  39. }
  40. return false
  41. }
  42. // Update updates internal state of what has been downloaded into the temporary
  43. // files by the remote device for this specific folder.
  44. func (p *deviceFolderDownloadState) Update(updates []protocol.FileDownloadProgressUpdate) {
  45. p.mut.Lock()
  46. defer p.mut.Unlock()
  47. for _, update := range updates {
  48. local, ok := p.files[update.Name]
  49. if update.UpdateType == protocol.FileDownloadProgressUpdateTypeForget && ok && local.version.Equal(update.Version) {
  50. delete(p.files, update.Name)
  51. } else if update.UpdateType == protocol.FileDownloadProgressUpdateTypeAppend {
  52. if !ok {
  53. local = deviceFolderFileDownloadState{
  54. blockIndexes: update.BlockIndexes,
  55. version: update.Version,
  56. blockSize: int(update.BlockSize),
  57. }
  58. } else if !local.version.Equal(update.Version) {
  59. local.blockIndexes = append(local.blockIndexes[:0], update.BlockIndexes...)
  60. local.version = update.Version
  61. local.blockSize = int(update.BlockSize)
  62. } else {
  63. local.blockIndexes = append(local.blockIndexes, update.BlockIndexes...)
  64. }
  65. p.files[update.Name] = local
  66. }
  67. }
  68. }
  69. func (p *deviceFolderDownloadState) BytesDownloaded() int64 {
  70. p.mut.RLock()
  71. defer p.mut.RUnlock()
  72. var res int64
  73. for _, state := range p.files {
  74. // BlockSize is a new field introduced in 1.4.1, thus a fallback
  75. // is required (will potentially underrepresent downloaded bytes).
  76. if state.blockSize != 0 {
  77. res += int64(len(state.blockIndexes) * state.blockSize)
  78. } else {
  79. res += int64(len(state.blockIndexes) * protocol.MinBlockSize)
  80. }
  81. }
  82. return res
  83. }
  84. // GetBlockCounts returns a map filename -> number of blocks downloaded.
  85. func (p *deviceFolderDownloadState) GetBlockCounts() map[string]int {
  86. p.mut.RLock()
  87. res := make(map[string]int, len(p.files))
  88. for name, state := range p.files {
  89. res[name] = len(state.blockIndexes)
  90. }
  91. p.mut.RUnlock()
  92. return res
  93. }
  94. // deviceDownloadState represents the state of all in progress downloads
  95. // for all folders of a specific device.
  96. type deviceDownloadState struct {
  97. mut sync.RWMutex
  98. folders map[string]*deviceFolderDownloadState
  99. }
  100. // Update updates internal state of what has been downloaded into the temporary
  101. // files by the remote device for this specific folder.
  102. func (t *deviceDownloadState) Update(folder string, updates []protocol.FileDownloadProgressUpdate) {
  103. if t == nil {
  104. return
  105. }
  106. t.mut.RLock()
  107. f, ok := t.folders[folder]
  108. t.mut.RUnlock()
  109. if !ok {
  110. f = &deviceFolderDownloadState{
  111. mut: sync.NewRWMutex(),
  112. files: make(map[string]deviceFolderFileDownloadState),
  113. }
  114. t.mut.Lock()
  115. t.folders[folder] = f
  116. t.mut.Unlock()
  117. }
  118. f.Update(updates)
  119. }
  120. // Has returns whether block at that specific index, and that specific version of the file
  121. // is currently available on the remote device for pulling from a temporary file.
  122. func (t *deviceDownloadState) Has(folder, file string, version protocol.Vector, index int) bool {
  123. if t == nil {
  124. return false
  125. }
  126. t.mut.RLock()
  127. f, ok := t.folders[folder]
  128. t.mut.RUnlock()
  129. if !ok {
  130. return false
  131. }
  132. return f.Has(file, version, index)
  133. }
  134. // GetBlockCounts returns a map filename -> number of blocks downloaded for the
  135. // given folder.
  136. func (t *deviceDownloadState) GetBlockCounts(folder string) map[string]int {
  137. if t == nil {
  138. return nil
  139. }
  140. t.mut.RLock()
  141. defer t.mut.RUnlock()
  142. for name, state := range t.folders {
  143. if name == folder {
  144. return state.GetBlockCounts()
  145. }
  146. }
  147. return nil
  148. }
  149. func (t *deviceDownloadState) BytesDownloaded(folder string) int64 {
  150. if t == nil {
  151. return 0
  152. }
  153. t.mut.RLock()
  154. defer t.mut.RUnlock()
  155. for name, state := range t.folders {
  156. if name == folder {
  157. return state.BytesDownloaded()
  158. }
  159. }
  160. return 0
  161. }
  162. func newDeviceDownloadState() *deviceDownloadState {
  163. return &deviceDownloadState{
  164. mut: sync.NewRWMutex(),
  165. folders: make(map[string]*deviceFolderDownloadState),
  166. }
  167. }