Simplify vflist_get_formatted()
[geeqie.git] / src / filecache.c
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 typedef struct _FileCacheEntry FileCacheEntry;
37 struct _FileCacheEntry {
38         FileData *fd;
39         gulong size;
40 };
41
42 static void file_cache_notify_cb(FileData *fd, NotifyType type, gpointer data);
43 static void file_cache_remove_fd(FileCacheData *fc, FileData *fd);
44
45 FileCacheData *file_cache_new(FileCacheReleaseFunc release, gulong max_size)
46 {
47         FileCacheData *fc = g_new(FileCacheData, 1);
48
49         fc->release = release;
50         fc->list = NULL;
51         fc->max_size = max_size;
52         fc->size = 0;
53
54         file_data_register_notify_func(file_cache_notify_cb, fc, NOTIFY_PRIORITY_HIGH);
55
56         return fc;
57 }
58
59 gboolean file_cache_get(FileCacheData *fc, FileData *fd)
60 {
61         GList *work;
62
63         g_assert(fc && fd);
64
65         work = fc->list;
66         while (work)
67                 {
68                 FileCacheEntry *fce = work->data;
69                 if (fce->fd == fd)
70                         {
71                         /* entry exists */
72                         DEBUG_2("cache hit: fc=%p %s", fc, fd->path);
73                         if (work == fc->list) return TRUE; /* already at the beginning */
74                         /* move it to the beginning */
75                         DEBUG_2("cache move to front: fc=%p %s", fc, fd->path);
76                         fc->list = g_list_remove_link(fc->list, work);
77                         fc->list = g_list_concat(work, fc->list);
78
79                         if (file_data_check_changed_files(fd)) {
80                                 /* file has been changed, cance entry is no longer valid */
81                                 file_cache_remove_fd(fc, fd);
82                                 return FALSE;
83                         }
84                         if (debug_file_cache) file_cache_dump(fc);
85                         return TRUE;
86                         }
87                 work = work->next;
88                 }
89         DEBUG_2("cache miss: fc=%p %s", fc, fd->path);
90         return FALSE;
91 }
92
93 void file_cache_set_size(FileCacheData *fc, gulong size)
94 {
95         GList *work;
96         FileCacheEntry *last_fe;
97
98         if (debug_file_cache) file_cache_dump(fc);
99
100         work = g_list_last(fc->list);
101         while (fc->size > size && work)
102                 {
103                 GList *prev;
104                 last_fe = work->data;
105                 prev = work->prev;
106                 fc->list = g_list_delete_link(fc->list, work);
107                 work = prev;
108
109                 DEBUG_2("file changed - cache remove: fc=%p %s", fc, last_fe->fd->path);
110                 fc->size -= last_fe->size;
111                 fc->release(last_fe->fd);
112                 file_data_unref(last_fe->fd);
113                 g_free(last_fe);
114                 }
115 }
116
117 void file_cache_put(FileCacheData *fc, FileData *fd, gulong size)
118 {
119         FileCacheEntry *fe;
120
121         if (file_cache_get(fc, fd)) return;
122
123         DEBUG_2("cache add: fc=%p %s", fc, fd->path);
124         fe = g_new(FileCacheEntry, 1);
125         fe->fd = file_data_ref(fd);
126         fe->size = size;
127         fc->list = g_list_prepend(fc->list, fe);
128         fc->size += size;
129
130         file_cache_set_size(fc, fc->max_size);
131 }
132
133 gulong file_cache_get_max_size(FileCacheData *fc)
134 {
135         return fc->max_size;
136 }
137
138 gulong file_cache_get_size(FileCacheData *fc)
139 {
140         return fc->size;
141 }
142
143 void file_cache_set_max_size(FileCacheData *fc, gulong size)
144 {
145         fc->max_size = size;
146         file_cache_set_size(fc, fc->max_size);
147 }
148
149 static void file_cache_remove_fd(FileCacheData *fc, FileData *fd)
150 {
151         GList *work;
152         FileCacheEntry *fe;
153
154         if (debug_file_cache) file_cache_dump(fc);
155
156         work = fc->list;
157         while (work)
158                 {
159                 GList *current = work;
160                 fe = work->data;
161                 work = work->next;
162
163                 if (fe->fd == fd)
164                         {
165                         fc->list = g_list_delete_link(fc->list, current);
166
167                         DEBUG_1("cache remove: fc=%p %s", fc, fe->fd->path);
168                         fc->size -= fe->size;
169                         fc->release(fe->fd);
170                         file_data_unref(fe->fd);
171                         g_free(fe);
172                         }
173                 }
174 }
175
176 void file_cache_dump(FileCacheData *fc)
177 {
178         GList *work = fc->list;
179         gulong n = 0;
180
181         DEBUG_1("cache dump: fc=%p max size:%ld size:%ld", fc, fc->max_size, fc->size);
182
183         while (work)
184                 {
185                 FileCacheEntry *fe = work->data;
186                 work = work->next;
187                 DEBUG_1("cache entry: fc=%p [%lu] %s %ld", fc, ++n, fe->fd->path, fe->size);
188                 }
189 }
190
191 static void file_cache_notify_cb(FileData *fd, NotifyType type, gpointer data)
192 {
193         FileCacheData *fc = data;
194
195         if (type & (NOTIFY_REREAD | NOTIFY_CHANGE)) /* invalidate the entry on each file change */
196                 {
197                 DEBUG_1("Notify cache: %s %04x", fd->path, type);
198                 file_cache_remove_fd(fc, fd);
199                 }
200 }
201 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */