diff --git a/cmd/syncthing/gui.go b/cmd/syncthing/gui.go
index f01b9ed3d..e58c909a0 100644
--- a/cmd/syncthing/gui.go
+++ b/cmd/syncthing/gui.go
@@ -72,7 +72,7 @@ type modelIntf interface {
Completion(device protocol.DeviceID, folder string) model.FolderCompletion
Override(folder string)
NeedFolderFiles(folder string, page, perpage int) ([]db.FileInfoTruncated, []db.FileInfoTruncated, []db.FileInfoTruncated, int)
- NeedSize(folder string) (nfiles, ndeletes int, bytes int64)
+ NeedSize(folder string) db.Counts
ConnectionStats() map[string]interface{}
DeviceStatistics() map[string]stats.DeviceStatistics
FolderStatistics() map[string]stats.FolderStatistics
@@ -90,8 +90,8 @@ type modelIntf interface {
ScanFolderSubdirs(folder string, subs []string) error
BringToFront(folder, file string)
ConnectedTo(deviceID protocol.DeviceID) bool
- GlobalSize(folder string) (nfiles, deleted int, bytes int64)
- LocalSize(folder string) (nfiles, deleted int, bytes int64)
+ GlobalSize(folder string) db.Counts
+ LocalSize(folder string) db.Counts
CurrentSequence(folder string) (int64, bool)
RemoteSequence(folder string) (int64, bool)
State(folder string) (string, time.Time, error)
@@ -634,16 +634,16 @@ func folderSummary(cfg configIntf, m modelIntf, folder string) map[string]interf
res["invalid"] = "" // Deprecated, retains external API for now
- globalFiles, globalDeleted, globalBytes := m.GlobalSize(folder)
- res["globalFiles"], res["globalDeleted"], res["globalBytes"] = globalFiles, globalDeleted, globalBytes
+ global := m.GlobalSize(folder)
+ res["globalFiles"], res["globalDirectories"], res["globalSymlinks"], res["globalDeleted"], res["globalBytes"] = global.Files, global.Directories, global.Symlinks, global.Deleted, global.Bytes
- localFiles, localDeleted, localBytes := m.LocalSize(folder)
- res["localFiles"], res["localDeleted"], res["localBytes"] = localFiles, localDeleted, localBytes
+ local := m.LocalSize(folder)
+ res["localFiles"], res["localDirectories"], res["localSymlinks"], res["localDeleted"], res["localBytes"] = local.Files, local.Directories, local.Symlinks, local.Deleted, local.Bytes
- needFiles, needDeletes, needBytes := m.NeedSize(folder)
- res["needFiles"], res["needDeletes"], res["needBytes"] = needFiles, needDeletes, needBytes
+ need := m.NeedSize(folder)
+ res["needFiles"], res["needDirectories"], res["needSymlinks"], res["needDeletes"], res["needBytes"] = need.Files, need.Directories, need.Symlinks, need.Deleted, need.Bytes
- res["inSyncFiles"], res["inSyncBytes"] = globalFiles-needFiles, globalBytes-needBytes
+ res["inSyncFiles"], res["inSyncBytes"] = global.Files-need.Files, global.Bytes-need.Bytes
var err error
res["state"], res["stateChanged"], err = m.State(folder)
diff --git a/cmd/syncthing/mocked_model_test.go b/cmd/syncthing/mocked_model_test.go
index 6c8c805e5..9fb4974a3 100644
--- a/cmd/syncthing/mocked_model_test.go
+++ b/cmd/syncthing/mocked_model_test.go
@@ -31,8 +31,8 @@ func (m *mockedModel) NeedFolderFiles(folder string, page, perpage int) ([]db.Fi
return nil, nil, nil, 0
}
-func (m *mockedModel) NeedSize(folder string) (nfiles, ndeletes int, bytes int64) {
- return 0, 0, 0
+func (m *mockedModel) NeedSize(folder string) db.Counts {
+ return db.Counts{}
}
func (m *mockedModel) ConnectionStats() map[string]interface{} {
@@ -95,12 +95,12 @@ func (m *mockedModel) ConnectedTo(deviceID protocol.DeviceID) bool {
return false
}
-func (m *mockedModel) GlobalSize(folder string) (nfiles, deleted int, bytes int64) {
- return 0, 0, 0
+func (m *mockedModel) GlobalSize(folder string) db.Counts {
+ return db.Counts{}
}
-func (m *mockedModel) LocalSize(folder string) (nfiles, deleted int, bytes int64) {
- return 0, 0, 0
+func (m *mockedModel) LocalSize(folder string) db.Counts {
+ return db.Counts{}
}
func (m *mockedModel) CurrentSequence(folder string) (int64, bool) {
diff --git a/cmd/syncthing/usage_report.go b/cmd/syncthing/usage_report.go
index 9cfb869a1..b72cad633 100644
--- a/cmd/syncthing/usage_report.go
+++ b/cmd/syncthing/usage_report.go
@@ -93,14 +93,14 @@ func reportData(cfg configIntf, m modelIntf) map[string]interface{} {
var totFiles, maxFiles int
var totBytes, maxBytes int64
for folderID := range cfg.Folders() {
- files, _, bytes := m.GlobalSize(folderID)
- totFiles += files
- totBytes += bytes
- if files > maxFiles {
- maxFiles = files
+ global := m.GlobalSize(folderID)
+ totFiles += global.Files
+ totBytes += global.Bytes
+ if global.Files > maxFiles {
+ maxFiles = global.Files
}
- if bytes > maxBytes {
- maxBytes = bytes
+ if global.Bytes > maxBytes {
+ maxBytes = global.Bytes
}
}
diff --git a/gui/default/index.html b/gui/default/index.html
index 6d77767ac..1a36299ed 100644
--- a/gui/default/index.html
+++ b/gui/default/index.html
@@ -310,16 +310,24 @@
Global State |
- {{model[folder.id].globalFiles | alwaysNumber}} items, ~{{model[folder.id].globalBytes | binary}}B |
+
+ {{model[folder.id].globalFiles | alwaysNumber}} files,
+ {{model[folder.id].globalDirectories | alwaysNumber}} directories,
+ ~{{model[folder.id].globalBytes | binary}}B
+ |
Local State |
- {{model[folder.id].localFiles | alwaysNumber}} items, ~{{model[folder.id].localBytes | binary}}B |
+
+ {{model[folder.id].localFiles | alwaysNumber}} files,
+ {{model[folder.id].localDirectories | alwaysNumber}} directories,
+ ~{{model[folder.id].localBytes | binary}}B
+ |
Out of Sync Items |
- {{model[folder.id].needFiles | alwaysNumber}} items, ~{{model[folder.id].needBytes | binary}}B
+ {{model[folder.id].needFiles+model[folder.id].needDirectories+model[folder.id].needSymlinks+model[folder.id].needDeletes | alwaysNumber}} items, ~{{model[folder.id].needBytes | binary}}B
|
diff --git a/lib/db/set.go b/lib/db/set.go
index f7daf5367..b7691fedc 100644
--- a/lib/db/set.go
+++ b/lib/db/set.go
@@ -51,11 +51,17 @@ type FileIntf interface {
// continue iteration, false to stop.
type Iterator func(f FileIntf) bool
+type Counts struct {
+ Files int
+ Directories int
+ Symlinks int
+ Deleted int
+ Bytes int64
+}
+
type sizeTracker struct {
- files int
- deleted int
- bytes int64
- mut stdsync.Mutex
+ Counts
+ mut stdsync.Mutex
}
func (s *sizeTracker) addFile(f FileIntf) {
@@ -64,12 +70,17 @@ func (s *sizeTracker) addFile(f FileIntf) {
}
s.mut.Lock()
- if f.IsDeleted() {
- s.deleted++
- } else {
- s.files++
+ switch {
+ case f.IsDeleted():
+ s.Deleted++
+ case f.IsDirectory():
+ s.Directories++
+ case f.IsSymlink():
+ s.Symlinks++
+ default:
+ s.Files++
}
- s.bytes += f.FileSize()
+ s.Bytes += f.FileSize()
s.mut.Unlock()
}
@@ -79,22 +90,27 @@ func (s *sizeTracker) removeFile(f FileIntf) {
}
s.mut.Lock()
- if f.IsDeleted() {
- s.deleted--
- } else {
- s.files--
+ switch {
+ case f.IsDeleted():
+ s.Deleted--
+ case f.IsDirectory():
+ s.Directories--
+ case f.IsSymlink():
+ s.Symlinks--
+ default:
+ s.Files--
}
- s.bytes -= f.FileSize()
- if s.deleted < 0 || s.files < 0 {
+ s.Bytes -= f.FileSize()
+ if s.Deleted < 0 || s.Files < 0 || s.Directories < 0 || s.Symlinks < 0 {
panic("bug: removed more than added")
}
s.mut.Unlock()
}
-func (s *sizeTracker) Size() (files, deleted int, bytes int64) {
+func (s *sizeTracker) Size() Counts {
s.mut.Lock()
defer s.mut.Unlock()
- return s.files, s.deleted, s.bytes
+ return s.Counts
}
func NewFileSet(folder string, db *Instance) *FileSet {
@@ -259,11 +275,11 @@ func (s *FileSet) Sequence(device protocol.DeviceID) int64 {
return s.remoteSequence[device]
}
-func (s *FileSet) LocalSize() (files, deleted int, bytes int64) {
+func (s *FileSet) LocalSize() Counts {
return s.localSize.Size()
}
-func (s *FileSet) GlobalSize() (files, deleted int, bytes int64) {
+func (s *FileSet) GlobalSize() Counts {
return s.globalSize.Size()
}
diff --git a/lib/db/set_test.go b/lib/db/set_test.go
index 9615f8143..9be802634 100644
--- a/lib/db/set_test.go
+++ b/lib/db/set_test.go
@@ -168,27 +168,33 @@ func TestGlobalSet(t *testing.T) {
t.Errorf("Global incorrect;\n A: %v !=\n E: %v", g, expectedGlobal)
}
- globalFiles, globalDeleted, globalBytes := 0, 0, int64(0)
+ globalFiles, globalDirectories, globalDeleted, globalBytes := 0, 0, 0, int64(0)
for _, f := range g {
if f.IsInvalid() {
continue
}
- if f.IsDeleted() {
+ switch {
+ case f.IsDeleted():
globalDeleted++
- } else {
+ case f.IsDirectory():
+ globalDirectories++
+ default:
globalFiles++
}
globalBytes += f.FileSize()
}
- gsFiles, gsDeleted, gsBytes := m.GlobalSize()
- if gsFiles != globalFiles {
- t.Errorf("Incorrect GlobalSize files; %d != %d", gsFiles, globalFiles)
+ gs := m.GlobalSize()
+ if gs.Files != globalFiles {
+ t.Errorf("Incorrect GlobalSize files; %d != %d", gs.Files, globalFiles)
}
- if gsDeleted != globalDeleted {
- t.Errorf("Incorrect GlobalSize deleted; %d != %d", gsDeleted, globalDeleted)
+ if gs.Directories != globalDirectories {
+ t.Errorf("Incorrect GlobalSize directories; %d != %d", gs.Directories, globalDirectories)
}
- if gsBytes != globalBytes {
- t.Errorf("Incorrect GlobalSize bytes; %d != %d", gsBytes, globalBytes)
+ if gs.Deleted != globalDeleted {
+ t.Errorf("Incorrect GlobalSize deleted; %d != %d", gs.Deleted, globalDeleted)
+ }
+ if gs.Bytes != globalBytes {
+ t.Errorf("Incorrect GlobalSize bytes; %d != %d", gs.Bytes, globalBytes)
}
h := fileList(haveList(m, protocol.LocalDeviceID))
@@ -198,27 +204,33 @@ func TestGlobalSet(t *testing.T) {
t.Errorf("Have incorrect;\n A: %v !=\n E: %v", h, localTot)
}
- haveFiles, haveDeleted, haveBytes := 0, 0, int64(0)
+ haveFiles, haveDirectories, haveDeleted, haveBytes := 0, 0, 0, int64(0)
for _, f := range h {
if f.IsInvalid() {
continue
}
- if f.IsDeleted() {
+ switch {
+ case f.IsDeleted():
haveDeleted++
- } else {
+ case f.IsDirectory():
+ haveDirectories++
+ default:
haveFiles++
}
haveBytes += f.FileSize()
}
- lsFiles, lsDeleted, lsBytes := m.LocalSize()
- if lsFiles != haveFiles {
- t.Errorf("Incorrect LocalSize files; %d != %d", lsFiles, haveFiles)
+ ls := m.LocalSize()
+ if ls.Files != haveFiles {
+ t.Errorf("Incorrect LocalSize files; %d != %d", ls.Files, haveFiles)
}
- if lsDeleted != haveDeleted {
- t.Errorf("Incorrect LocalSize deleted; %d != %d", lsDeleted, haveDeleted)
+ if ls.Directories != haveDirectories {
+ t.Errorf("Incorrect LocalSize directories; %d != %d", ls.Directories, haveDirectories)
}
- if lsBytes != haveBytes {
- t.Errorf("Incorrect LocalSize bytes; %d != %d", lsBytes, haveBytes)
+ if ls.Deleted != haveDeleted {
+ t.Errorf("Incorrect LocalSize deleted; %d != %d", ls.Deleted, haveDeleted)
+ }
+ if ls.Bytes != haveBytes {
+ t.Errorf("Incorrect LocalSize bytes; %d != %d", ls.Bytes, haveBytes)
}
h = fileList(haveList(m, remoteDevice0))
diff --git a/lib/model/model.go b/lib/model/model.go
index c4970b69b..fec754de8 100644
--- a/lib/model/model.go
+++ b/lib/model/model.go
@@ -476,7 +476,7 @@ func (m *Model) Completion(device protocol.DeviceID, folder string) FolderComple
return FolderCompletion{} // Folder doesn't exist, so we hardly have any of it
}
- _, _, tot := rf.GlobalSize()
+ tot := rf.GlobalSize().Bytes
if tot == 0 {
// Folder is empty, so we have all of it
return FolderCompletion{
@@ -531,42 +531,49 @@ func (m *Model) Completion(device protocol.DeviceID, folder string) FolderComple
}
}
-func sizeOfFile(f db.FileIntf) (files, deleted int, bytes int64) {
- if !f.IsDeleted() {
- files++
- } else {
- deleted++
+func addSizeOfFile(s *db.Counts, f db.FileIntf) {
+ switch {
+ case f.IsDeleted():
+ s.Deleted++
+ case f.IsDirectory():
+ s.Directories++
+ case f.IsSymlink():
+ s.Symlinks++
+ default:
+ s.Files++
}
- bytes += f.FileSize()
+ s.Bytes += f.FileSize()
return
}
// GlobalSize returns the number of files, deleted files and total bytes for all
// files in the global model.
-func (m *Model) GlobalSize(folder string) (nfiles, deleted int, bytes int64) {
+func (m *Model) GlobalSize(folder string) db.Counts {
m.fmut.RLock()
defer m.fmut.RUnlock()
if rf, ok := m.folderFiles[folder]; ok {
- nfiles, deleted, bytes = rf.GlobalSize()
+ return rf.GlobalSize()
}
- return
+ return db.Counts{}
}
// LocalSize returns the number of files, deleted files and total bytes for all
// files in the local folder.
-func (m *Model) LocalSize(folder string) (nfiles, deleted int, bytes int64) {
+func (m *Model) LocalSize(folder string) db.Counts {
m.fmut.RLock()
defer m.fmut.RUnlock()
if rf, ok := m.folderFiles[folder]; ok {
- nfiles, deleted, bytes = rf.LocalSize()
+ return rf.LocalSize()
}
- return
+ return db.Counts{}
}
// NeedSize returns the number and total size of currently needed files.
-func (m *Model) NeedSize(folder string) (nfiles, ndeletes int, bytes int64) {
+func (m *Model) NeedSize(folder string) db.Counts {
m.fmut.RLock()
defer m.fmut.RUnlock()
+
+ var result db.Counts
if rf, ok := m.folderFiles[folder]; ok {
ignores := m.folderIgnores[folder]
cfg := m.folderCfgs[folder]
@@ -575,16 +582,13 @@ func (m *Model) NeedSize(folder string) (nfiles, ndeletes int, bytes int64) {
return true
}
- fs, de, by := sizeOfFile(f)
- nfiles += fs
- ndeletes += de
- bytes += by
+ addSizeOfFile(&result, f)
return true
})
}
- bytes -= m.progressEmitter.BytesCompleted(folder)
- l.Debugf("%v NeedSize(%q): %d %d", m, folder, nfiles, bytes)
- return
+ result.Bytes -= m.progressEmitter.BytesCompleted(folder)
+ l.Debugf("%v NeedSize(%q): %v", m, folder, result)
+ return result
}
// NeedFolderFiles returns paginated list of currently needed files in
diff --git a/lib/model/model_test.go b/lib/model/model_test.go
index f4e36a88a..2b1f3180c 100644
--- a/lib/model/model_test.go
+++ b/lib/model/model_test.go
@@ -1324,8 +1324,8 @@ func TestIssue3028(t *testing.T) {
// Get a count of how many files are there now
- locorigfiles, _, _ := m.LocalSize("default")
- globorigfiles, _, _ := m.GlobalSize("default")
+ locorigfiles := m.LocalSize("default").Files
+ globorigfiles := m.GlobalSize("default").Files
// Delete and rescan specifically these two
@@ -1336,19 +1336,19 @@ func TestIssue3028(t *testing.T) {
// Verify that the number of files decreased by two and the number of
// deleted files increases by two
- locnowfiles, locdelfiles, _ := m.LocalSize("default")
- globnowfiles, globdelfiles, _ := m.GlobalSize("default")
- if locnowfiles != locorigfiles-2 {
- t.Errorf("Incorrect local accounting; got %d current files, expected %d", locnowfiles, locorigfiles-2)
+ loc := m.LocalSize("default")
+ glob := m.GlobalSize("default")
+ if loc.Files != locorigfiles-2 {
+ t.Errorf("Incorrect local accounting; got %d current files, expected %d", loc.Files, locorigfiles-2)
}
- if globnowfiles != globorigfiles-2 {
- t.Errorf("Incorrect global accounting; got %d current files, expected %d", globnowfiles, globorigfiles-2)
+ if glob.Files != globorigfiles-2 {
+ t.Errorf("Incorrect global accounting; got %d current files, expected %d", glob.Files, globorigfiles-2)
}
- if locdelfiles != 2 {
- t.Errorf("Incorrect local accounting; got %d deleted files, expected 2", locdelfiles)
+ if loc.Deleted != 2 {
+ t.Errorf("Incorrect local accounting; got %d deleted files, expected 2", loc.Deleted)
}
- if globdelfiles != 2 {
- t.Errorf("Incorrect global accounting; got %d deleted files, expected 2", globdelfiles)
+ if glob.Deleted != 2 {
+ t.Errorf("Incorrect global accounting; got %d deleted files, expected 2", glob.Deleted)
}
}
@@ -1722,14 +1722,14 @@ func TestIssue3496(t *testing.T) {
t.Log(comp)
// Check that NeedSize does the correct thing
- files, deletes, bytes := m.NeedSize("default")
- if files != 1 || bytes != 1234 {
+ need := m.NeedSize("default")
+ if need.Files != 1 || need.Bytes != 1234 {
// The one we added synthetically above
- t.Errorf("Incorrect need size; %d, %d != 1, 1234", files, bytes)
+ t.Errorf("Incorrect need size; %d, %d != 1, 1234", need.Files, need.Bytes)
}
- if deletes != len(localFiles)-1 {
+ if need.Deleted != len(localFiles)-1 {
// The rest
- t.Errorf("Incorrect need deletes; %d != %d", deletes, len(localFiles)-1)
+ t.Errorf("Incorrect need deletes; %d != %d", need.Deleted, len(localFiles)-1)
}
}