source: filezilla/trunk/fuentes/src/engine/directorycache.cpp @ 3185

Last change on this file since 3185 was 3185, checked in by jrpelegrina, 3 years ago

Update new version: 3.15.02

File size: 12.6 KB
Line 
1#include <filezilla.h>
2#include "directorycache.h"
3
4CDirectoryCache::CDirectoryCache()
5{
6}
7
8CDirectoryCache::~CDirectoryCache()
9{
10        for( auto & serverEntry : m_serverList ) {
11                for (auto & cacheEntry : serverEntry.cacheList ) {
12#ifdef __WXDEBUG__
13                        m_totalFileCount -= cacheEntry.listing.GetCount();
14#endif
15                        tLruList::iterator* lruIt = (tLruList::iterator*)cacheEntry.lruIt;
16                        if (lruIt) {
17                                m_leastRecentlyUsedList.erase(*lruIt);
18                                delete lruIt;
19                        }
20                }
21        }
22#ifdef __WXDEBUG__
23        wxASSERT(m_totalFileCount == 0);
24#endif
25}
26
27void CDirectoryCache::Store(const CDirectoryListing &listing, const CServer &server)
28{
29        fz::scoped_lock lock(mutex_);
30
31        tServerIter sit = CreateServerEntry(server);
32        wxASSERT(sit != m_serverList.end());
33
34        m_totalFileCount += listing.GetCount();
35
36        tCacheIter cit;
37        bool unused;
38        if (Lookup(cit, sit, listing.path, true, unused)) {
39                auto & entry = const_cast<CCacheEntry&>(*cit);
40                entry.modificationTime = fz::monotonic_clock::now();
41
42                m_totalFileCount -= cit->listing.GetCount();
43                entry.listing = listing;
44
45                return;
46        }
47
48        cit = sit->cacheList.emplace_hint(cit, listing);
49
50        UpdateLru(sit, cit);
51
52        Prune();
53}
54
55bool CDirectoryCache::Lookup(CDirectoryListing &listing, const CServer &server, const CServerPath &path, bool allowUnsureEntries, bool& is_outdated)
56{
57        fz::scoped_lock lock(mutex_);
58
59        tServerIter sit = GetServerEntry(server);
60        if (sit == m_serverList.end())
61                return false;
62
63        tCacheIter iter;
64        if (Lookup(iter, sit, path, allowUnsureEntries, is_outdated)) {
65                listing = iter->listing;
66                return true;
67        }
68
69        return false;
70}
71
72bool CDirectoryCache::Lookup(tCacheIter &cacheIter, tServerIter &sit, const CServerPath &path, bool allowUnsureEntries, bool& is_outdated)
73{
74        static CCacheEntry dummy;
75        dummy.listing.path = path;
76        cacheIter = sit->cacheList.lower_bound(dummy);
77
78        if (cacheIter != sit->cacheList.end()) {
79                CCacheEntry const& entry = *cacheIter;
80
81                if (entry.listing.path == path) {
82                        UpdateLru(sit, cacheIter);
83
84                        if (!allowUnsureEntries && entry.listing.get_unsure_flags())
85                                return false;
86
87                        is_outdated = (fz::monotonic_clock::now() - entry.listing.m_firstListTime).get_seconds() > CACHE_TIMEOUT;
88                        return true;
89                }
90        }
91
92        return false;
93}
94
95bool CDirectoryCache::DoesExist(const CServer &server, const CServerPath &path, int &hasUnsureEntries, bool &is_outdated)
96{
97        fz::scoped_lock lock(mutex_);
98
99        tServerIter sit = GetServerEntry(server);
100        if (sit == m_serverList.end())
101                return false;
102
103        tCacheIter iter;
104        if (Lookup(iter, sit, path, true, is_outdated)) {
105                hasUnsureEntries = iter->listing.get_unsure_flags();
106                return true;
107        }
108
109        return false;
110}
111
112bool CDirectoryCache::LookupFile(CDirentry &entry, const CServer &server, const CServerPath &path, const wxString& file, bool &dirDidExist, bool &matchedCase)
113{
114        fz::scoped_lock lock(mutex_);
115
116        tServerIter sit = GetServerEntry(server);
117        if (sit == m_serverList.end()) {
118                dirDidExist = false;
119                return false;
120        }
121
122        tCacheIter iter;
123        bool unused;
124        if (!Lookup(iter, sit, path, true, unused)) {
125                dirDidExist = false;
126                return false;
127        }
128        dirDidExist = true;
129
130        const CCacheEntry &cacheEntry = *iter;
131        const CDirectoryListing &listing = cacheEntry.listing;
132
133        int i = listing.FindFile_CmpCase(file);
134        if (i >= 0) {
135                entry = listing[i];
136                matchedCase = true;
137                return true;
138        }
139        i = listing.FindFile_CmpNoCase(file);
140        if (i >= 0) {
141                entry = listing[i];
142                matchedCase = false;
143                return true;
144        }
145
146        return false;
147}
148
149bool CDirectoryCache::InvalidateFile(const CServer &server, const CServerPath &path, const wxString& filename, bool *wasDir /*=false*/)
150{
151        fz::scoped_lock lock(mutex_);
152
153        tServerIter sit = GetServerEntry(server);
154        if (sit == m_serverList.end())
155                return false;
156
157        for (tCacheIter iter = sit->cacheList.begin(); iter != sit->cacheList.end(); ++iter) {
158                auto & entry = const_cast<CCacheEntry&>(*iter);
159                if (path.CmpNoCase(entry.listing.path))
160                        continue;
161
162                UpdateLru(sit, iter);
163
164                for (unsigned int i = 0; i < entry.listing.GetCount(); i++) {
165                        if (!filename.CmpNoCase(((const CCacheEntry&)entry).listing[i].name)) {
166                                if (wasDir)
167                                        *wasDir = entry.listing[i].is_dir();
168                                entry.listing[i].flags |= CDirentry::flag_unsure;
169                        }
170                }
171                entry.listing.m_flags |= CDirectoryListing::unsure_unknown;
172                entry.modificationTime = fz::monotonic_clock::now();
173        }
174
175        return true;
176}
177
178bool CDirectoryCache::UpdateFile(const CServer &server, const CServerPath &path, const wxString& filename, bool mayCreate, enum Filetype type /*=file*/, int64_t size /*=-1*/)
179{
180        fz::scoped_lock lock(mutex_);
181
182        tServerIter sit = GetServerEntry(server);
183        if (sit == m_serverList.end())
184                return false;
185
186        bool updated = false;
187
188        for (tCacheIter iter = sit->cacheList.begin(); iter != sit->cacheList.end(); ++iter) {
189                auto & entry = const_cast<CCacheEntry&>(*iter);
190                if (path.CmpNoCase(entry.listing.path))
191                        continue;
192
193                UpdateLru(sit, iter);
194
195                bool matchCase = false;
196                unsigned int i;
197                for (i = 0; i < entry.listing.GetCount(); ++i) {
198                        if (!filename.CmpNoCase(entry.listing[i].name)) {
199                                entry.listing[i].flags |= CDirentry::flag_unsure;
200                                if (entry.listing[i].name == filename) {
201                                        matchCase = true;
202                                        break;
203                                }
204                        }
205                }
206
207                if (matchCase) {
208                        enum Filetype old_type = entry.listing[i].is_dir() ? dir : file;
209                        if (type != old_type)
210                                entry.listing.m_flags |= CDirectoryListing::unsure_invalid;
211                        else if (type == dir)
212                                entry.listing.m_flags |= CDirectoryListing::unsure_dir_changed;
213                        else
214                                entry.listing.m_flags |= CDirectoryListing::unsure_file_changed;
215                }
216                else if (type != unknown && mayCreate) {
217                        const unsigned int count = entry.listing.GetCount();
218                        entry.listing.SetCount(count + 1);
219                        CDirentry& direntry = entry.listing[count];
220                        direntry.name = filename;
221                        if (type == dir)
222                                direntry.flags = CDirentry::flag_dir | CDirentry::flag_unsure;
223                        else
224                                direntry.flags = CDirentry::flag_unsure;
225                        direntry.size = size;
226                        switch (type) {
227                        case dir:
228                                entry.listing.m_flags |= CDirectoryListing::unsure_dir_added | CDirectoryListing::listing_has_dirs;
229                                break;
230                        case file:
231                                entry.listing.m_flags |= CDirectoryListing::unsure_file_added;
232                                break;
233                        default:
234                                entry.listing.m_flags |= CDirectoryListing::unsure_invalid;
235                                break;
236                        }
237
238                        ++m_totalFileCount;
239                }
240                else
241                        entry.listing.m_flags |= CDirectoryListing::unsure_unknown;
242                entry.modificationTime = fz::monotonic_clock::now();
243
244                updated = true;
245        }
246
247        return updated;
248}
249
250bool CDirectoryCache::RemoveFile(const CServer &server, const CServerPath &path, const wxString& filename)
251{
252        fz::scoped_lock lock(mutex_);
253
254        tServerIter sit = GetServerEntry(server);
255        if (sit == m_serverList.end())
256                return false;
257
258        for (tCacheIter iter = sit->cacheList.begin(); iter != sit->cacheList.end(); ++iter) {
259                auto & entry = const_cast<CCacheEntry&>(*iter);
260                if (path.CmpNoCase(entry.listing.path))
261                        continue;
262
263                UpdateLru(sit, iter);
264
265                bool matchCase = false;
266                for (unsigned int i = 0; i < entry.listing.GetCount(); ++i) {
267                        if (entry.listing[i].name == filename)
268                                matchCase = true;
269                }
270
271                if (matchCase) {
272                        unsigned int i;
273                        for (i = 0; i < entry.listing.GetCount(); i++)
274                                if (entry.listing[i].name == filename)
275                                        break;
276                        wxASSERT(i != entry.listing.GetCount());
277
278                        entry.listing.RemoveEntry(i); // This does set m_hasUnsureEntries
279                        --m_totalFileCount;
280                }
281                else {
282                        for (unsigned int i = 0; i < entry.listing.GetCount(); ++i) {
283                                if (!filename.CmpNoCase(entry.listing[i].name))
284                                        entry.listing[i].flags |= CDirentry::flag_unsure;
285                        }
286                        entry.listing.m_flags |= CDirectoryListing::unsure_invalid;
287                }
288                entry.modificationTime = fz::monotonic_clock::now();
289        }
290
291        return true;
292}
293
294void CDirectoryCache::InvalidateServer(const CServer& server)
295{
296        fz::scoped_lock lock(mutex_);
297
298        for (auto iter = m_serverList.begin(); iter != m_serverList.end(); ++iter)
299        {
300                if (iter->server != server)
301                        continue;
302
303                for (tCacheIter cit = iter->cacheList.begin(); cit != iter->cacheList.end(); ++cit)
304                {
305                        tLruList::iterator* lruIt = (tLruList::iterator*)cit->lruIt;
306                        if (lruIt)
307                        {
308                                m_leastRecentlyUsedList.erase(*lruIt);
309                                delete lruIt;
310                        }
311
312                        m_totalFileCount -= cit->listing.GetCount();
313                }
314
315                m_serverList.erase(iter);
316                break;
317        }
318}
319
320bool CDirectoryCache::GetChangeTime(fz::monotonic_clock& time, const CServer &server, const CServerPath &path)
321{
322        fz::scoped_lock lock(mutex_);
323
324        tServerIter sit = GetServerEntry(server);
325        if (sit == m_serverList.end())
326                return false;
327
328        tCacheIter iter;
329        bool unused;
330        if (Lookup(iter, sit, path, true, unused)) {
331                time = iter->modificationTime;
332                return true;
333        }
334
335        return false;
336}
337
338void CDirectoryCache::RemoveDir(const CServer& server, const CServerPath& path, const wxString& filename, const CServerPath&)
339{
340        fz::scoped_lock lock(mutex_);
341
342        // TODO: This is not 100% foolproof and may not work properly
343        // Perhaps just throw away the complete cache?
344
345        tServerIter sit = GetServerEntry(server);
346        if (sit == m_serverList.end())
347                return;
348
349        CServerPath absolutePath = path;
350        if (!absolutePath.AddSegment(filename))
351                absolutePath.clear();
352
353        for (tCacheIter iter = sit->cacheList.begin(); iter != sit->cacheList.end(); ) {
354                auto & entry = const_cast<CCacheEntry&>(*iter);
355                // Delete exact matches and subdirs
356                if (!absolutePath.empty() && (entry.listing.path == absolutePath || absolutePath.IsParentOf(entry.listing.path, true))) {
357                        m_totalFileCount -= entry.listing.GetCount();
358                        tLruList::iterator* lruIt = (tLruList::iterator*)iter->lruIt;
359                        if (lruIt) {
360                                m_leastRecentlyUsedList.erase(*lruIt);
361                                delete lruIt;
362                        }
363                        sit->cacheList.erase(iter++);
364                }
365                else {
366                        ++iter;
367                }
368        }
369
370        RemoveFile(server, path, filename);
371}
372
373void CDirectoryCache::Rename(const CServer& server, const CServerPath& pathFrom, const wxString& fileFrom, const CServerPath& pathTo, const wxString& fileTo)
374{
375        fz::scoped_lock lock(mutex_);
376
377        tServerIter sit = GetServerEntry(server);
378        if (sit == m_serverList.end())
379                return;
380
381        tCacheIter iter;
382        bool is_outdated = false;
383        bool found = Lookup(iter, sit, pathFrom, true, is_outdated);
384        if (found) {
385                auto & listing = const_cast<CDirectoryListing&>(iter->listing);
386                if (pathFrom == pathTo)
387                {
388                        RemoveFile(server, pathFrom, fileTo);
389                        unsigned int i;
390                        for (i = 0; i < listing.GetCount(); i++)
391                        {
392                                if (listing[i].name == fileFrom)
393                                        break;
394                        }
395                        if (i != listing.GetCount())
396                        {
397                                if (listing[i].is_dir())
398                                {
399                                        RemoveDir(server, pathFrom, fileFrom, CServerPath());
400                                        RemoveDir(server, pathFrom, fileTo, CServerPath());
401                                        UpdateFile(server, pathFrom, fileTo, true, dir);
402                                }
403                                else
404                                {
405                                        listing[i].name = fileTo;
406                                        listing[i].flags |= CDirentry::flag_unsure;
407                                        listing.m_flags |= CDirectoryListing::unsure_unknown;
408                                        listing.ClearFindMap();
409                                }
410                        }
411                        return;
412                }
413                else {
414                        unsigned int i;
415                        for (i = 0; i < listing.GetCount(); i++) {
416                                if (listing[i].name == fileFrom)
417                                        break;
418                        }
419                        if (i != listing.GetCount()) {
420                                if (listing[i].is_dir()) {
421                                        RemoveDir(server, pathFrom, fileFrom, CServerPath());
422                                        UpdateFile(server, pathTo, fileTo, true, dir);
423                                }
424                                else {
425                                        RemoveFile(server, pathFrom, fileFrom);
426                                        UpdateFile(server, pathTo, fileTo, true, file);
427                                }
428                        }
429                        return;
430                }
431        }
432
433        // We know nothing, be on the safe side and invalidate everything.
434        InvalidateServer(server);
435}
436
437CDirectoryCache::tServerIter CDirectoryCache::CreateServerEntry(const CServer& server)
438{
439        for (tServerIter iter = m_serverList.begin(); iter != m_serverList.end(); ++iter) {
440                if (iter->server == server)
441                        return iter;
442        }
443        m_serverList.emplace_back(server);
444
445        return --m_serverList.end();
446}
447
448CDirectoryCache::tServerIter CDirectoryCache::GetServerEntry(const CServer& server)
449{
450        tServerIter iter;
451        for (iter = m_serverList.begin(); iter != m_serverList.end(); ++iter) {
452                if (iter->server == server)
453                        break;
454        }
455
456        return iter;
457}
458
459void CDirectoryCache::UpdateLru(tServerIter const& sit, tCacheIter const& cit)
460{
461        tLruList::iterator* lruIt = (tLruList::iterator*)cit->lruIt;
462        if (lruIt) {
463                m_leastRecentlyUsedList.splice(m_leastRecentlyUsedList.end(), m_leastRecentlyUsedList, *lruIt);
464                **lruIt = std::make_pair(sit, cit);
465        }
466        else {
467                auto & entry = const_cast<CCacheEntry&>(*cit);
468                entry.lruIt = (void*)new tLruList::iterator(m_leastRecentlyUsedList.emplace(m_leastRecentlyUsedList.end(), sit, cit));
469        }
470}
471
472void CDirectoryCache::Prune()
473{
474        while ((m_leastRecentlyUsedList.size() > 50000) ||
475                (m_totalFileCount > 1000000 && m_leastRecentlyUsedList.size() > 1000) ||
476                (m_totalFileCount > 5000000 && m_leastRecentlyUsedList.size() > 100))
477        {
478                tFullEntryPosition pos = m_leastRecentlyUsedList.front();
479                tLruList::iterator* lruIt = (tLruList::iterator*)pos.second->lruIt;
480                delete lruIt;
481
482                m_totalFileCount -= pos.second->listing.GetCount();
483
484                pos.first->cacheList.erase(pos.second);
485                if (pos.first->cacheList.empty())
486                        m_serverList.erase(pos.first);
487
488                m_leastRecentlyUsedList.pop_front();
489        }
490}
Note: See TracBrowser for help on using the repository browser.