set.go 4.77 KB
Newer Older
Jakob Borg's avatar
Jakob Borg committed
1 2 3
// Copyright (C) 2014 Jakob Borg and Contributors (see the CONTRIBUTORS file).
// All rights reserved. Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.
Jakob Borg's avatar
Jakob Borg committed
4

5 6 7 8 9 10
// Package files provides a set type to track local/remote files with newness
// checks. We must do a certain amount of normalization in here. We will get
// fed paths with either native or wire-format separators and encodings
// depending on who calls us. We transform paths to wire-format (NFC and
// slashes) on the way to the database, and transform to native format
// (varying separator and encoding) on the way back out.
11 12 13 14 15
package files

import (
	"sync"

16 17
	"github.com/syncthing/syncthing/lamport"
	"github.com/syncthing/syncthing/protocol"
Jakob Borg's avatar
Jakob Borg committed
18
	"github.com/syndtr/goleveldb/leveldb"
19 20 21
)

type fileRecord struct {
Jakob Borg's avatar
Jakob Borg committed
22
	File   protocol.FileInfo
23 24
	Usage  int
	Global bool
25 26 27 28 29
}

type bitset uint64

type Set struct {
30 31 32 33
	localVersion map[protocol.NodeID]uint64
	mutex        sync.Mutex
	repo         string
	db           *leveldb.DB
34 35
}

Jakob Borg's avatar
Jakob Borg committed
36 37
func NewSet(repo string, db *leveldb.DB) *Set {
	var s = Set{
38 39 40
		localVersion: make(map[protocol.NodeID]uint64),
		repo:         repo,
		db:           db,
41
	}
42

Jakob Borg's avatar
Jakob Borg committed
43
	var nodeID protocol.NodeID
44
	ldbWithAllRepoTruncated(db, []byte(repo), func(node []byte, f protocol.FileInfoTruncated) bool {
Jakob Borg's avatar
Jakob Borg committed
45 46 47
		copy(nodeID[:], node)
		if f.LocalVersion > s.localVersion[nodeID] {
			s.localVersion[nodeID] = f.LocalVersion
48
		}
Jakob Borg's avatar
Jakob Borg committed
49
		lamport.Default.Tick(f.Version)
50 51
		return true
	})
Jakob Borg's avatar
Jakob Borg committed
52 53 54 55
	if debug {
		l.Debugf("loaded localVersion for %q: %#v", repo, s.localVersion)
	}
	clock(s.localVersion[protocol.LocalNodeID])
56

Jakob Borg's avatar
Jakob Borg committed
57
	return &s
58 59
}

Jakob Borg's avatar
Jakob Borg committed
60
func (s *Set) Replace(node protocol.NodeID, fs []protocol.FileInfo) {
61
	if debug {
Jakob Borg's avatar
Jakob Borg committed
62
		l.Debugf("%s Replace(%v, [%d])", s.repo, node, len(fs))
63
	}
64
	normalizeFilenames(fs)
Jakob Borg's avatar
Jakob Borg committed
65 66
	s.mutex.Lock()
	defer s.mutex.Unlock()
67
	s.localVersion[node] = ldbReplace(s.db, []byte(s.repo), node[:], fs)
68 69
}

Jakob Borg's avatar
Jakob Borg committed
70
func (s *Set) ReplaceWithDelete(node protocol.NodeID, fs []protocol.FileInfo) {
71
	if debug {
Jakob Borg's avatar
Jakob Borg committed
72
		l.Debugf("%s ReplaceWithDelete(%v, [%d])", s.repo, node, len(fs))
73
	}
74
	normalizeFilenames(fs)
Jakob Borg's avatar
Jakob Borg committed
75 76
	s.mutex.Lock()
	defer s.mutex.Unlock()
77 78
	if lv := ldbReplaceWithDelete(s.db, []byte(s.repo), node[:], fs); lv > s.localVersion[node] {
		s.localVersion[node] = lv
79 80 81
	}
}

Jakob Borg's avatar
Jakob Borg committed
82
func (s *Set) Update(node protocol.NodeID, fs []protocol.FileInfo) {
83
	if debug {
Jakob Borg's avatar
Jakob Borg committed
84
		l.Debugf("%s Update(%v, [%d])", s.repo, node, len(fs))
85
	}
86
	normalizeFilenames(fs)
Jakob Borg's avatar
Jakob Borg committed
87 88
	s.mutex.Lock()
	defer s.mutex.Unlock()
89 90
	if lv := ldbUpdate(s.db, []byte(s.repo), node[:], fs); lv > s.localVersion[node] {
		s.localVersion[node] = lv
91 92 93
	}
}

Jakob Borg's avatar
Jakob Borg committed
94
func (s *Set) WithNeed(node protocol.NodeID, fn fileIterator) {
95
	if debug {
96
		l.Debugf("%s WithNeed(%v)", s.repo, node)
97
	}
98
	ldbWithNeed(s.db, []byte(s.repo), node[:], false, nativeFileIterator(fn))
99 100 101 102 103 104
}

func (s *Set) WithNeedTruncated(node protocol.NodeID, fn fileIterator) {
	if debug {
		l.Debugf("%s WithNeedTruncated(%v)", s.repo, node)
	}
105
	ldbWithNeed(s.db, []byte(s.repo), node[:], true, nativeFileIterator(fn))
106 107
}

Jakob Borg's avatar
Jakob Borg committed
108
func (s *Set) WithHave(node protocol.NodeID, fn fileIterator) {
109
	if debug {
Jakob Borg's avatar
Jakob Borg committed
110
		l.Debugf("%s WithHave(%v)", s.repo, node)
111
	}
112
	ldbWithHave(s.db, []byte(s.repo), node[:], false, nativeFileIterator(fn))
113 114 115 116 117 118
}

func (s *Set) WithHaveTruncated(node protocol.NodeID, fn fileIterator) {
	if debug {
		l.Debugf("%s WithHaveTruncated(%v)", s.repo, node)
	}
119
	ldbWithHave(s.db, []byte(s.repo), node[:], true, nativeFileIterator(fn))
120 121
}

Jakob Borg's avatar
Jakob Borg committed
122 123 124 125
func (s *Set) WithGlobal(fn fileIterator) {
	if debug {
		l.Debugf("%s WithGlobal()", s.repo)
	}
126
	ldbWithGlobal(s.db, []byte(s.repo), false, nativeFileIterator(fn))
Jakob Borg's avatar
Jakob Borg committed
127 128
}

129
func (s *Set) WithGlobalTruncated(fn fileIterator) {
130
	if debug {
Jakob Borg's avatar
Jakob Borg committed
131
		l.Debugf("%s WithGlobalTruncated()", s.repo)
132
	}
133
	ldbWithGlobal(s.db, []byte(s.repo), true, nativeFileIterator(fn))
134 135
}

Jakob Borg's avatar
Jakob Borg committed
136
func (s *Set) Get(node protocol.NodeID, file string) protocol.FileInfo {
137 138 139
	f := ldbGet(s.db, []byte(s.repo), node[:], []byte(normalizedFilename(file)))
	f.Name = nativeFilename(f.Name)
	return f
140 141
}

Jakob Borg's avatar
Jakob Borg committed
142
func (s *Set) GetGlobal(file string) protocol.FileInfo {
143 144 145
	f := ldbGetGlobal(s.db, []byte(s.repo), []byte(normalizedFilename(file)))
	f.Name = nativeFilename(f.Name)
	return f
146 147
}

Jakob Borg's avatar
Jakob Borg committed
148
func (s *Set) Availability(file string) []protocol.NodeID {
149
	return ldbAvailability(s.db, []byte(s.repo), []byte(normalizedFilename(file)))
150 151
}

152
func (s *Set) LocalVersion(node protocol.NodeID) uint64 {
153 154
	s.mutex.Lock()
	defer s.mutex.Unlock()
155
	return s.localVersion[node]
156
}
157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177

func normalizeFilenames(fs []protocol.FileInfo) {
	for i := range fs {
		fs[i].Name = normalizedFilename(fs[i].Name)
	}
}

func nativeFileIterator(fn fileIterator) fileIterator {
	return func(fi protocol.FileIntf) bool {
		switch f := fi.(type) {
		case protocol.FileInfo:
			f.Name = nativeFilename(f.Name)
			return fn(f)
		case protocol.FileInfoTruncated:
			f.Name = nativeFilename(f.Name)
			return fn(f)
		default:
			panic("unknown interface type")
		}
	}
}