clang-tidy: misc-redundant-expression
[geeqie.git] / src / filecache.cc
1 /*
2  * Copyright (C) 2008 - 2016 The Geeqie Team
3  *
4  * Author: Vladimir Nadvornik
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with this program; if not, write to the Free Software Foundation, Inc.,
18  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19  */
20
21 #include "main.h"
22 #include "filecache.h"
23
24 /* Set to TRUE to add file cache dumps to the debug output */
25 const gboolean debug_file_cache = FALSE;
26
27 /* this implements a simple LRU algorithm */
28
29 struct FileCacheData {
30         FileCacheReleaseFunc release;
31         GList *list;
32         gulong max_size;
33         gulong size;
34 };
35
36 struct FileCacheEntry {
37         FileData *fd;
38         gulong size;
39 };
40
41 static void file_cache_notify_cb(FileData *fd, NotifyType type, gpointer data);
42 static void file_cache_remove_fd(FileCacheData *fc, FileData *fd);
43
44 FileCacheData *file_cache_new(FileCacheReleaseFunc release, gulong max_size)
45 {
46         auto fc = g_new(FileCacheData, 1);
47
48         fc->release = release;
49         fc->list = nullptr;
50         fc->max_size = max_size;
51         fc->size = 0;
52
53         file_data_register_notify_func(file_cache_notify_cb, fc, NOTIFY_PRIORITY_HIGH);
54
55         return fc;
56 }
57
58 gboolean file_cache_get(FileCacheData *fc, FileData *fd)
59 {
60         GList *work;
61
62         g_assert(fc && fd);
63
64         work = fc->list;
65         while (work)
66                 {
67                 auto fce = static_cast<FileCacheEntry *>(work->data);
68                 if (fce->fd == fd)
69                         {
70                         /* entry exists */
71                         DEBUG_2("cache hit: fc=%p %s", (void *)fc, fd->path);
72                         if (work == fc->list) return TRUE; /* already at the beginning */
73                         /* move it to the beginning */
74                         DEBUG_2("cache move to front: fc=%p %s", (void *)fc, fd->path);
75                         fc->list = g_list_remove_link(fc->list, work);
76                         fc->list = g_list_concat(work, fc->list);
77
78                         if (file_data_check_changed_files(fd)) {
79                                 /* file has been changed, cance entry is no longer valid */
80                                 file_cache_remove_fd(fc, fd);
81                                 return FALSE;
82                         }
83                         if (debug_file_cache) file_cache_dump(fc);
84                         return TRUE;
85                         }
86                 work = work->next;
87                 }
88         DEBUG_2("cache miss: fc=%p %s", (void *)fc, fd->path);
89         return FALSE;
90 }
91
92 void file_cache_set_size(FileCacheData *fc, gulong size)
93 {
94         GList *work;
95         FileCacheEntry *last_fe;
96
97         if (debug_file_cache) file_cache_dump(fc);
98
99         work = g_list_last(fc->list);
100         while (fc->size > size && work)
101                 {
102                 GList *prev;
103                 last_fe = static_cast<FileCacheEntry *>(work->data);
104                 prev = work->prev;
105                 fc->list = g_list_delete_link(fc->list, work);
106                 work = prev;
107
108                 DEBUG_2("file changed - cache remove: fc=%p %s", (void *)fc, last_fe->fd->path);
109                 fc->size -= last_fe->size;
110                 fc->release(last_fe->fd);
111                 file_data_unref(last_fe->fd);
112                 g_free(last_fe);
113                 }
114 }
115
116 void file_cache_put(FileCacheData *fc, FileData *fd, gulong size)
117 {
118         FileCacheEntry *fe;
119
120         if (file_cache_get(fc, fd)) return;
121
122         DEBUG_2("cache add: fc=%p %s", (void *)fc, fd->path);
123         fe = g_new(FileCacheEntry, 1);
124         fe->fd = file_data_ref(fd);
125         fe->size = size;
126         fc->list = g_list_prepend(fc->list, fe);
127         fc->size += size;
128
129         file_cache_set_size(fc, fc->max_size);
130 }
131
132 #pragma GCC diagnostic push
133 #pragma GCC diagnostic ignored "-Wunused-function"
134 gulong file_cache_get_max_size_unused(FileCacheData *fc)
135 {
136         return fc->max_size;
137 }
138
139 gulong file_cache_get_size_unused(FileCacheData *fc)
140 {
141         return fc->size;
142 }
143 #pragma GCC diagnostic pop
144
145 void file_cache_set_max_size(FileCacheData *fc, gulong size)
146 {
147         fc->max_size = size;
148         file_cache_set_size(fc, fc->max_size);
149 }
150
151 static void file_cache_remove_fd(FileCacheData *fc, FileData *fd)
152 {
153         GList *work;
154         FileCacheEntry *fe;
155
156         if (debug_file_cache) file_cache_dump(fc);
157
158         work = fc->list;
159         while (work)
160                 {
161                 GList *current = work;
162                 fe = static_cast<FileCacheEntry *>(work->data);
163                 work = work->next;
164
165                 if (fe->fd == fd)
166                         {
167                         fc->list = g_list_delete_link(fc->list, current);
168
169                         DEBUG_1("cache remove: fc=%p %s", (void *)fc, fe->fd->path);
170                         fc->size -= fe->size;
171                         fc->release(fe->fd);
172                         file_data_unref(fe->fd);
173                         g_free(fe);
174                         }
175                 }
176 }
177
178 void file_cache_dump(FileCacheData *fc)
179 {
180         GList *work = fc->list;
181         gulong n = 0;
182
183         DEBUG_1("cache dump: fc=%p max size:%ld size:%ld", (void *)fc, fc->max_size, fc->size);
184
185         while (work)
186                 {
187                 auto fe = static_cast<FileCacheEntry *>(work->data);
188                 work = work->next;
189                 DEBUG_1("cache entry: fc=%p [%lu] %s %ld", (void *)fc, ++n, fe->fd->path, fe->size);
190                 }
191 }
192
193 static void file_cache_notify_cb(FileData *fd, NotifyType type, gpointer data)
194 {
195         auto fc = static_cast<FileCacheData *>(data);
196
197         if (type & (NOTIFY_REREAD | NOTIFY_CHANGE)) /* invalidate the entry on each file change */
198                 {
199                 DEBUG_1("Notify cache: %s %04x", fd->path, type);
200                 file_cache_remove_fd(fc, fd);
201                 }
202 }
203 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */