devicedownloadstate.go 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  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 []int32
  17. version protocol.Vector
  18. }
  19. // deviceFolderDownloadState holds current download state of all files that
  20. // a remote device is currently downloading in a specific folder.
  21. type deviceFolderDownloadState struct {
  22. mut sync.RWMutex
  23. files map[string]deviceFolderFileDownloadState
  24. }
  25. // Has returns whether a block at that specific index, and that specific version of the file
  26. // is currently available on the remote device for pulling from a temporary file.
  27. func (p *deviceFolderDownloadState) Has(file string, version protocol.Vector, index int32) bool {
  28. p.mut.RLock()
  29. defer p.mut.RUnlock()
  30. local, ok := p.files[file]
  31. if !ok || !local.version.Equal(version) {
  32. return false
  33. }
  34. for _, existingIndex := range local.blockIndexes {
  35. if existingIndex == index {
  36. return true
  37. }
  38. }
  39. return false
  40. }
  41. // Update updates internal state of what has been downloaded into the temporary
  42. // files by the remote device for this specific folder.
  43. func (p *deviceFolderDownloadState) Update(updates []protocol.FileDownloadProgressUpdate) {
  44. p.mut.Lock()
  45. defer p.mut.Unlock()
  46. for _, update := range updates {
  47. local, ok := p.files[update.Name]
  48. if update.UpdateType == protocol.UpdateTypeForget && ok && local.version.Equal(update.Version) {
  49. delete(p.files, update.Name)
  50. } else if update.UpdateType == protocol.UpdateTypeAppend {
  51. if !ok {
  52. local = deviceFolderFileDownloadState{
  53. blockIndexes: update.BlockIndexes,
  54. version: update.Version,
  55. }
  56. } else if !local.version.Equal(update.Version) {
  57. local.blockIndexes = append(local.blockIndexes[:0], update.BlockIndexes...)
  58. local.version = update.Version
  59. } else {
  60. local.blockIndexes = append(local.blockIndexes, update.BlockIndexes...)
  61. }
  62. p.files[update.Name] = local
  63. }
  64. }
  65. }
  66. // GetBlockCounts returns a map filename -> number of blocks downloaded.
  67. func (p *deviceFolderDownloadState) GetBlockCounts() map[string]int {
  68. p.mut.RLock()
  69. res := make(map[string]int, len(p.files))
  70. for name, state := range p.files {
  71. res[name] = len(state.blockIndexes)
  72. }
  73. p.mut.RUnlock()
  74. return res
  75. }
  76. // deviceDownloadState represents the state of all in progress downloads
  77. // for all folders of a specific device.
  78. type deviceDownloadState struct {
  79. mut sync.RWMutex
  80. folders map[string]*deviceFolderDownloadState
  81. }
  82. // Update updates internal state of what has been downloaded into the temporary
  83. // files by the remote device for this specific folder.
  84. func (t *deviceDownloadState) Update(folder string, updates []protocol.FileDownloadProgressUpdate) {
  85. if t == nil {
  86. return
  87. }
  88. t.mut.RLock()
  89. f, ok := t.folders[folder]
  90. t.mut.RUnlock()
  91. if !ok {
  92. f = &deviceFolderDownloadState{
  93. mut: sync.NewRWMutex(),
  94. files: make(map[string]deviceFolderFileDownloadState),
  95. }
  96. t.mut.Lock()
  97. t.folders[folder] = f
  98. t.mut.Unlock()
  99. }
  100. f.Update(updates)
  101. }
  102. // Has returns whether block at that specific index, and that specific version of the file
  103. // is currently available on the remote device for pulling from a temporary file.
  104. func (t *deviceDownloadState) Has(folder, file string, version protocol.Vector, index int32) bool {
  105. if t == nil {
  106. return false
  107. }
  108. t.mut.RLock()
  109. f, ok := t.folders[folder]
  110. t.mut.RUnlock()
  111. if !ok {
  112. return false
  113. }
  114. return f.Has(file, version, index)
  115. }
  116. // GetBlockCounts returns a map filename -> number of blocks downloaded for the
  117. // given folder.
  118. func (t *deviceDownloadState) GetBlockCounts(folder string) map[string]int {
  119. if t == nil {
  120. return nil
  121. }
  122. t.mut.RLock()
  123. defer t.mut.RUnlock()
  124. for name, state := range t.folders {
  125. if name == folder {
  126. return state.GetBlockCounts()
  127. }
  128. }
  129. return nil
  130. }
  131. func newDeviceDownloadState() *deviceDownloadState {
  132. return &deviceDownloadState{
  133. mut: sync.NewRWMutex(),
  134. folders: make(map[string]*deviceFolderDownloadState),
  135. }
  136. }