devicedownloadstate.go 5.2 KB

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