GCC Code Coverage Report


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 68.0% 266 / 0 / 391
Functions: 82.9% 29 / 0 / 35
Branches: 35.0% 98 / 0 / 280

shumate/shumate-file-cache.c
Line Branch Exec Source
1 /*
2 * Copyright (C) 2009 Pierre-Luc Beaudoin <pierre-luc@pierlux.com>
3 * Copyright (C) 2010-2013 Jiri Techet <techet@gmail.com>
4 * Copyright (C) 2019 Marcus Lundblad <ml@update.uu.se>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library 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 GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 /**
22 * ShumateFileCache:
23 *
24 * A cache that stores and retrieves tiles from the file system. It is mainly
25 * used by [class@TileDownloader], but can also be used by custom data
26 * sources.
27 *
28 * The cache will be filled up to a certain size limit. When this limit is
29 * reached, the cache can be purged, and the tiles that are accessed least are
30 * deleted.
31 *
32 * ## ETags
33 *
34 * The cache can optionally store an ETag string with each tile. This is
35 * useful to avoid redownloading old tiles that haven't changed (for example,
36 * using the HTTP If-None-Match header).
37 */
38
39 #include "shumate-file-cache.h"
40
41 #include <sqlite3.h>
42 #include <errno.h>
43 #include <glib.h>
44 #include <gio/gio.h>
45 #include <string.h>
46 #include <stdlib.h>
47
48 enum
49 {
50 PROP_0,
51 PROP_SIZE_LIMIT,
52 PROP_CACHE_DIR,
53 PROP_CACHE_KEY,
54 N_PROPS
55 };
56
57 static GParamSpec *properties[N_PROPS];
58
59 struct _ShumateFileCache
60 {
61 GObject parent_instance;
62
63 guint size_limit;
64 char *cache_dir;
65 char *cache_key;
66
67 sqlite3 *db;
68 sqlite3_stmt *stmt_select;
69 sqlite3_stmt *stmt_update;
70
71 int size_estimate;
72 gboolean have_size_estimate;
73 gboolean purge_in_progress;
74 };
75
76
4/6
shumate_file_cache_class_intern_init:
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 8 times.
shumate_file_cache_get_type:
✓ Branch 2 → 3 taken 8 times.
✓ Branch 2 → 7 taken 62 times.
✓ Branch 4 → 5 taken 8 times.
✗ Branch 4 → 7 not taken.
86 G_DEFINE_TYPE (ShumateFileCache, shumate_file_cache, G_TYPE_OBJECT);
77
78
79 typedef char sqlite_str;
80
1/4
✗ Branch 25 → 26 not taken.
✗ Branch 25 → 27 not taken.
✗ Branch 48 → 49 not taken.
✓ Branch 48 → 50 taken 1 time.
2 G_DEFINE_AUTOPTR_CLEANUP_FUNC (sqlite_str, sqlite3_free);
81 1 G_DEFINE_AUTOPTR_CLEANUP_FUNC (sqlite3_stmt, sqlite3_finalize);
82
83
84 static void finalize_sql (ShumateFileCache *file_cache);
85 static void init_cache (ShumateFileCache *file_cache);
86 static char *get_filename (ShumateFileCache *file_cache,
87 int x,
88 int y,
89 int zoom_level);
90 static void delete_tile (ShumateFileCache *file_cache,
91 const char *filename);
92 static gboolean create_cache_dir (const char *dir_name);
93
94 static void on_tile_filled (ShumateFileCache *self,
95 int x,
96 int y,
97 int zoom_level);
98
99 static void
100 ✗ shumate_file_cache_get_property (GObject *object,
101 guint property_id,
102 GValue *value,
103 GParamSpec *pspec)
104 {
105 ✗ ShumateFileCache *self = SHUMATE_FILE_CACHE (object);
106
107 ✗ switch (property_id)
108 {
109 ✗ case PROP_SIZE_LIMIT:
110 ✗ g_value_set_uint (value, shumate_file_cache_get_size_limit (self));
111 ✗ break;
112
113 ✗ case PROP_CACHE_DIR:
114 ✗ g_value_set_string (value, shumate_file_cache_get_cache_dir (self));
115 ✗ break;
116
117 ✗ case PROP_CACHE_KEY:
118 ✗ g_value_set_string (value, shumate_file_cache_get_cache_key (self));
119 ✗ break;
120
121 ✗ default:
122 ✗ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
123 }
124 ✗ }
125
126
127 static void
128 78 shumate_file_cache_set_property (GObject *object,
129 guint property_id,
130 const GValue *value,
131 GParamSpec *pspec)
132 {
133 78 ShumateFileCache *self = SHUMATE_FILE_CACHE (object);
134
135
3/4
✓ Branch 2 → 3 taken 26 times.
✓ Branch 2 → 6 taken 26 times.
✓ Branch 2 → 19 taken 26 times.
✗ Branch 2 → 32 not taken.
78 switch (property_id)
136 {
137 26 case PROP_SIZE_LIMIT:
138 26 shumate_file_cache_set_size_limit (self, g_value_get_uint (value));
139 26 break;
140
141 26 case PROP_CACHE_DIR:
142
1/2
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 26 times.
26 g_free (self->cache_dir);
143
1/2
✗ Branch 10 → 11 not taken.
✓ Branch 10 → 12 taken 26 times.
26 self->cache_dir = g_strdup (g_value_get_string (value));
144 26 break;
145
146 26 case PROP_CACHE_KEY:
147
1/2
✗ Branch 19 → 20 not taken.
✓ Branch 19 → 21 taken 26 times.
26 g_free (self->cache_key);
148
1/2
✗ Branch 23 → 24 not taken.
✓ Branch 23 → 25 taken 26 times.
26 self->cache_key = g_strdup (g_value_get_string (value));
149 26 break;
150
151 ✗ default:
152 ✗ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
153 }
154 78 }
155
156 static void
157 26 finalize_sql (ShumateFileCache *self)
158 {
159
2/2
✓ Branch 2 → 3 taken 16 times.
✓ Branch 2 → 4 taken 10 times.
26 g_clear_pointer (&self->stmt_select, sqlite3_finalize);
160
2/2
✓ Branch 4 → 5 taken 16 times.
✓ Branch 4 → 6 taken 10 times.
26 g_clear_pointer (&self->stmt_update, sqlite3_finalize);
161
162
1/2
✓ Branch 6 → 7 taken 26 times.
✗ Branch 6 → 11 not taken.
26 if (self->db)
163 {
164 26 int error = sqlite3_close (self->db);
165
1/2
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 10 taken 26 times.
26 if (error != SQLITE_OK)
166 ✗ g_debug ("Sqlite returned error %d when closing cache.db", error);
167 26 self->db = NULL;
168 }
169 26 }
170
171
172 static void
173 26 shumate_file_cache_finalize (GObject *object)
174 {
175 26 ShumateFileCache *self = SHUMATE_FILE_CACHE (object);
176
177 26 finalize_sql (self);
178
179
1/2
✓ Branch 3 → 4 taken 26 times.
✗ Branch 3 → 5 not taken.
26 g_clear_pointer (&self->cache_dir, g_free);
180
1/2
✓ Branch 5 → 6 taken 26 times.
✗ Branch 5 → 7 not taken.
26 g_clear_pointer (&self->cache_key, g_free);
181
182 26 G_OBJECT_CLASS (shumate_file_cache_parent_class)->finalize (object);
183 26 }
184
185
186 static gboolean
187 26 create_cache_dir (const char *dir_name)
188 {
189 /* If needed, create the cache's dirs */
190
1/2
✓ Branch 2 → 3 taken 26 times.
✗ Branch 2 → 8 not taken.
26 if (dir_name)
191 {
192
1/4
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 8 taken 26 times.
✗ Branch 5 → 6 not taken.
✗ Branch 5 → 8 not taken.
26 if (g_mkdir_with_parents (dir_name, 0700) == -1 && errno != EEXIST)
193 {
194 ✗ g_warning ("Unable to create the image cache path '%s': %s",
195 dir_name, g_strerror (errno));
196 ✗ return FALSE;
197 }
198 }
199 return TRUE;
200 }
201
202
203 static void
204 26 init_cache (ShumateFileCache *self)
205 {
206 26 char *filename = NULL;
207 26 char *error_msg = NULL;
208 26 gint error;
209
210
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 26 times.
36 g_return_if_fail (create_cache_dir (self->cache_dir));
211
212 26 filename = g_build_filename (self->cache_dir,
213 "cache.db", NULL);
214
215 /* Make sure the database is opened in serialized mode (OPEN_FULLMUTEX)
216 * because shumate_file_cache_purge_cache_async() runs in a separate thread.
217 * See <https://sqlite.org/threadsafe.html> */
218 26 error = sqlite3_open_v2 (filename, &self->db,
219 SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX, NULL);
220
1/2
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 9 taken 26 times.
26 g_free (filename);
221
222
1/2
✗ Branch 10 → 11 not taken.
✓ Branch 10 → 12 taken 26 times.
26 if (error == SQLITE_ERROR)
223 {
224 ✗ g_debug ("Sqlite returned error %d when opening cache.db", error);
225 ✗ return;
226 }
227
228 26 sqlite3_exec (self->db,
229 "PRAGMA synchronous=OFF;"
230 "PRAGMA auto_vacuum=INCREMENTAL;",
231 NULL, NULL, &error_msg);
232
2/2
✓ Branch 13 → 14 taken 10 times.
✓ Branch 13 → 17 taken 16 times.
26 if (error_msg != NULL)
233 {
234 10 g_debug ("Set PRAGMA: %s", error_msg);
235 10 sqlite3_free (error_msg);
236 10 return;
237 }
238
239 16 sqlite3_exec (self->db,
240 "CREATE TABLE IF NOT EXISTS tiles ("
241 "filename TEXT PRIMARY KEY, "
242 "etag TEXT, "
243 "popularity INT DEFAULT 1, "
244 "size INT DEFAULT 0)",
245 NULL, NULL, &error_msg);
246
1/2
✗ Branch 18 → 19 not taken.
✓ Branch 18 → 22 taken 16 times.
16 if (error_msg != NULL)
247 {
248 ✗ g_debug ("Creating table 'tiles' failed: %s", error_msg);
249 ✗ sqlite3_free (error_msg);
250 ✗ return;
251 }
252
253 16 error = sqlite3_prepare_v2 (self->db,
254 "SELECT etag FROM tiles WHERE filename = ?", -1,
255 &self->stmt_select, NULL);
256
1/2
✗ Branch 23 → 24 not taken.
✓ Branch 23 → 27 taken 16 times.
16 if (error != SQLITE_OK)
257 {
258 ✗ self->stmt_select = NULL;
259 ✗ g_debug ("Failed to prepare the select Etag statement, error:%d: %s",
260 error, sqlite3_errmsg (self->db));
261 ✗ return;
262 }
263
264 16 error = sqlite3_prepare_v2 (self->db,
265 "UPDATE tiles SET popularity = popularity + 1 WHERE filename = ?", -1,
266 &self->stmt_update, NULL);
267
1/2
✗ Branch 28 → 29 not taken.
✓ Branch 28 → 32 taken 16 times.
16 if (error != SQLITE_OK)
268 {
269 ✗ self->stmt_update = NULL;
270 ✗ g_debug ("Failed to prepare the update popularity statement, error: %s",
271 sqlite3_errmsg (self->db));
272 ✗ return;
273 }
274
275 16 g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_CACHE_DIR]);
276 }
277
278
279 static void
280 26 shumate_file_cache_constructed (GObject *object)
281 {
282 26 ShumateFileCache *self = SHUMATE_FILE_CACHE (object);
283
284
1/2
✓ Branch 2 → 3 taken 26 times.
✗ Branch 2 → 6 not taken.
26 if (!self->cache_dir)
285 {
286 26 self->cache_dir = g_build_path (G_DIR_SEPARATOR_S,
287 g_get_user_cache_dir (),
288 "shumate", NULL);
289 }
290
291 26 init_cache (self);
292
293 26 G_OBJECT_CLASS (shumate_file_cache_parent_class)->constructed (object);
294 26 }
295
296
297 static void
298 8 shumate_file_cache_class_init (ShumateFileCacheClass *klass)
299 {
300 8 GObjectClass *object_class = G_OBJECT_CLASS (klass);
301
302 8 object_class->finalize = shumate_file_cache_finalize;
303 8 object_class->get_property = shumate_file_cache_get_property;
304 8 object_class->set_property = shumate_file_cache_set_property;
305 8 object_class->constructed = shumate_file_cache_constructed;
306
307 /**
308 * ShumateFileCache:size-limit:
309 *
310 * The cache size limit in bytes.
311 *
312 * Note: this new value will not be applied until you call shumate_file_cache_purge()
313 */
314 16 properties[PROP_SIZE_LIMIT] =
315 8 g_param_spec_uint ("size-limit", NULL, NULL,
316 1,
317 G_MAXINT,
318 100000000,
319 G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
320
321 /**
322 * ShumateFileCache:cache-dir:
323 *
324 * The directory where the tile database is stored.
325 */
326 16 properties[PROP_CACHE_DIR] =
327 8 g_param_spec_string ("cache-dir", NULL, NULL,
328 NULL,
329 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
330
331 /**
332 * ShumateFileCache:cache-key:
333 *
334 * The key used to store and retrieve tiles from the cache. Different keys
335 * can be used to store multiple tilesets in the same cache directory.
336 */
337 16 properties[PROP_CACHE_KEY] =
338 8 g_param_spec_string ("cache-key", NULL, NULL,
339 NULL,
340 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
341
342 8 g_object_class_install_properties (object_class, N_PROPS, properties);
343 8 }
344
345
346 static void
347 26 shumate_file_cache_init (ShumateFileCache *self)
348 {
349 26 self->cache_dir = NULL;
350 26 self->size_limit = 100000000;
351 26 self->size_estimate = 0;
352 26 self->have_size_estimate = FALSE;
353 26 self->cache_dir = NULL;
354 26 self->db = NULL;
355 26 self->stmt_select = NULL;
356 26 self->stmt_update = NULL;
357 26 }
358
359
360 /**
361 * shumate_file_cache_new_full:
362 * @size_limit: maximum size of the cache in bytes
363 * @cache_key: an ID for the tileset to store/retrieve
364 * @cache_dir: (allow-none): the directory where the cache is created. When cache_dir == NULL,
365 * a cache in ~/.cache/shumate is used.
366 *
367 * Constructor of #ShumateFileCache.
368 *
369 * Returns: a constructed #ShumateFileCache
370 */
371 ShumateFileCache *
372 26 shumate_file_cache_new_full (guint size_limit,
373 const char *cache_key,
374 const char *cache_dir)
375 {
376 26 ShumateFileCache *cache;
377
378
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 26 times.
26 g_return_val_if_fail (cache_key != NULL, NULL);
379
380 26 cache = g_object_new (SHUMATE_TYPE_FILE_CACHE,
381 "size-limit", size_limit,
382 "cache-key", cache_key,
383 "cache-dir", cache_dir,
384 NULL);
385 26 return cache;
386 }
387
388
389 /**
390 * shumate_file_cache_get_size_limit:
391 * @self: a #ShumateFileCache
392 *
393 * Gets the cache size limit in bytes.
394 *
395 * Returns: size limit
396 */
397 guint
398 ✗ shumate_file_cache_get_size_limit (ShumateFileCache *self)
399 {
400 ✗ g_return_val_if_fail (SHUMATE_IS_FILE_CACHE (self), 0);
401
402 ✗ return self->size_limit;
403 }
404
405
406 /**
407 * shumate_file_cache_get_cache_dir:
408 * @self: a #ShumateFileCache
409 *
410 * Gets the directory where the cache database is stored.
411 *
412 * Returns: the directory
413 */
414 const char *
415 ✗ shumate_file_cache_get_cache_dir (ShumateFileCache *self)
416 {
417 ✗ g_return_val_if_fail (SHUMATE_IS_FILE_CACHE (self), NULL);
418
419 ✗ return self->cache_dir;
420 }
421
422
423 /**
424 * shumate_file_cache_get_cache_key:
425 * @self: a #ShumateFileCache
426 *
427 * Gets the key used to store and retrieve tiles from the cache. Different keys
428 * can be used to store multiple tilesets in the same cache directory.
429 *
430 * Returns: the cache key
431 */
432 const char *
433 5 shumate_file_cache_get_cache_key (ShumateFileCache *self)
434 {
435
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 5 times.
5 g_return_val_if_fail (SHUMATE_IS_FILE_CACHE (self), NULL);
436
437 5 return self->cache_key;
438 }
439
440
441 /**
442 * shumate_file_cache_set_size_limit:
443 * @self: a #ShumateFileCache
444 * @size_limit: the cache limit in bytes
445 *
446 * Sets the cache size limit in bytes.
447 */
448 void
449 26 shumate_file_cache_set_size_limit (ShumateFileCache *self,
450 guint size_limit)
451 {
452
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 26 times.
26 g_return_if_fail (SHUMATE_IS_FILE_CACHE (self));
453
454 26 self->size_limit = size_limit;
455 26 g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SIZE_LIMIT]);
456 }
457
458
459 static char *
460 5 get_filename (ShumateFileCache *self,
461 int x,
462 int y,
463 int zoom_level)
464 {
465 5 const char *cache_key;
466
467
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 5 times.
5 g_return_val_if_fail (SHUMATE_IS_FILE_CACHE (self), NULL);
468
1/2
✓ Branch 5 → 6 taken 5 times.
✗ Branch 5 → 9 not taken.
5 g_return_val_if_fail (self->cache_dir, NULL);
469
470 5 cache_key = shumate_file_cache_get_cache_key (self);
471
472 5 char *filename = g_strdup_printf ("%s" G_DIR_SEPARATOR_S
473 "%s" G_DIR_SEPARATOR_S
474 "%d" G_DIR_SEPARATOR_S
475 "%d" G_DIR_SEPARATOR_S "%d.png",
476 self->cache_dir,
477 cache_key,
478 zoom_level,
479 x,
480 y);
481 5 return filename;
482 }
483
484
485 static char *
486 1 db_get_etag (ShumateFileCache *self,
487 int x,
488 int y,
489 int zoom_level)
490 {
491 1 int sql_rc = SQLITE_OK;
492 1 g_autofree char *filename = get_filename (self, x, y, zoom_level);
493
494 1 sqlite3_reset (self->stmt_select);
495 1 sql_rc = sqlite3_bind_text (self->stmt_select, 1, filename, -1, SQLITE_STATIC);
496
1/2
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 9 taken 1 time.
1 if (sql_rc == SQLITE_ERROR)
497 {
498 ✗ g_debug ("Failed to prepare the SQL query for finding the Etag of '%s', error: %s",
499 filename, sqlite3_errmsg (self->db));
500 ✗ return NULL;
501 }
502
503 1 sql_rc = sqlite3_step (self->stmt_select);
504
1/2
✓ Branch 10 → 11 taken 1 time.
✗ Branch 10 → 20 not taken.
1 if (sql_rc == SQLITE_ROW)
505 {
506 1 const char *etag = (const char *) sqlite3_column_text (self->stmt_select, 0);
507
1/2
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 14 taken 1 time.
2 return g_strdup (etag);
508 }
509 ✗ else if (sql_rc == SQLITE_DONE)
510 {
511 ✗ g_debug ("'%s' doesn't have an etag",
512 filename);
513 }
514 ✗ else if (sql_rc == SQLITE_ERROR)
515 {
516 ✗ g_debug ("Failed to finding the Etag of '%s', %d error: %s",
517 filename, sql_rc, sqlite3_errmsg (self->db));
518 }
519
520 return NULL;
521 }
522
523
524 /**
525 * shumate_file_cache_mark_up_to_date:
526 * @self: a #ShumateFileCache
527 * @x: the X coordinate of the tile
528 * @y: the Y coordinate of the tile
529 * @zoom_level: the zoom level of the tile
530 *
531 * Marks a tile in the cache as being up to date, without changing its data.
532 *
533 * For example, a network source might call this function when it gets an HTTP
534 * 304 Not Modified response.
535 */
536 void
537 ✗ shumate_file_cache_mark_up_to_date (ShumateFileCache *self,
538 int x,
539 int y,
540 int zoom_level)
541 {
542 ✗ g_autofree char *filename = NULL;
543 ✗ g_autoptr(GFile) file = NULL;
544 ✗ g_autoptr(GFileInfo) info = NULL;
545
546 ✗ g_return_if_fail (SHUMATE_IS_FILE_CACHE (self));
547
548 ✗ filename = get_filename (self, x, y, zoom_level);
549 ✗ file = g_file_new_for_path (filename);
550
551 ✗ info = g_file_query_info (file, G_FILE_ATTRIBUTE_TIME_MODIFIED,
552 G_FILE_QUERY_INFO_NONE, NULL, NULL);
553
554 ✗ if (info)
555 {
556 ✗ g_autoptr(GDateTime) now = g_date_time_new_now_utc ();
557
558 ✗ g_file_info_set_modification_date_time (info, now);
559 ✗ g_file_set_attributes_from_info (file, info, G_FILE_QUERY_INFO_NONE, NULL, NULL);
560 }
561 }
562
563
564 static void
565 1 on_tile_filled (ShumateFileCache *self,
566 int x,
567 int y,
568 int zoom_level)
569 {
570 1 int sql_rc = SQLITE_OK;
571 1 g_autofree char *filename = NULL;
572
573 1 filename = get_filename (self, x, y, zoom_level);
574
575 1 g_debug ("popularity of %s", filename);
576
577 1 sqlite3_reset (self->stmt_update);
578 1 sql_rc = sqlite3_bind_text (self->stmt_update, 1, filename, -1, SQLITE_STATIC);
579
1/2
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 9 taken 1 time.
1 if (sql_rc != SQLITE_OK)
580 {
581 ✗ g_debug ("Failed to set values to the popularity query of '%s', error: %s",
582 filename, sqlite3_errmsg (self->db));
583 ✗ return;
584 }
585
586 1 sql_rc = sqlite3_step (self->stmt_update);
587
1/2
✓ Branch 10 → 11 taken 1 time.
✗ Branch 10 → 14 not taken.
1 if (sql_rc != SQLITE_DONE)
588 {
589 /* may not be present in this cache */
590 return;
591 }
592 }
593
594
595 static void
596 ✗ delete_tile (ShumateFileCache *self,
597 const char *filename)
598 {
599 ✗ g_return_if_fail (SHUMATE_IS_FILE_CACHE (self));
600 ✗ g_autoptr(sqlite_str) query = NULL;
601 ✗ g_autoptr(sqlite_str) sql_error = NULL;
602 ✗ g_autoptr(GError) gerror = NULL;
603 ✗ g_autoptr(GFile) file = NULL;
604
605 ✗ file = g_file_new_for_path (filename);
606 ✗ if (!g_file_delete (file, NULL, &gerror))
607 {
608 ✗ g_debug ("Deleting tile from disk failed: %s", gerror->message);
609 }
610
611 ✗ query = sqlite3_mprintf ("DELETE FROM tiles WHERE filename = %Q", filename);
612 ✗ sqlite3_exec (self->db, query, NULL, NULL, &sql_error);
613 ✗ if (sql_error != NULL)
614 {
615 ✗ g_debug ("Deleting tile from db failed: %s", sql_error);
616 }
617 }
618
619
620 static void
621 1 purge_cache (GTask *task,
622 gpointer source_object,
623 gpointer _task_data,
624 GCancellable *cancellable)
625 {
626 1 ShumateFileCache *self = (ShumateFileCache *) source_object;
627
628 1 char *query;
629 1 g_autoptr(sqlite3_stmt) stmt = NULL;
630 1 int rc = 0;
631 1 guint original_size, current_size = 0;
632 1 guint highest_popularity = 0;
633
1/4
✗ Branch 44 → 45 not taken.
✗ Branch 44 → 46 not taken.
✓ Branch 50 → 51 taken 1 time.
✗ Branch 50 → 52 not taken.
1 g_autoptr(sqlite_str) error = NULL;
634
635 1 query = "SELECT SUM (size) FROM tiles";
636 1 rc = sqlite3_prepare (self->db, query, strlen (query), &stmt, NULL);
637
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 7 taken 1 time.
1 if (rc != SQLITE_OK)
638 {
639 ✗ g_warning ("Can't compute cache size %s", sqlite3_errmsg (self->db));
640 ✗ g_task_return_boolean (task, FALSE);
641 ✗ return;
642 }
643
644 1 rc = sqlite3_step (stmt);
645
1/2
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 13 taken 1 time.
1 if (rc != SQLITE_ROW)
646 {
647 ✗ g_warning ("Failed to count the total cache consumption %s",
648 sqlite3_errmsg (self->db));
649 ✗ g_task_return_boolean (task, FALSE);
650 ✗ return;
651 }
652
653 1 current_size = sqlite3_column_int (stmt, 0);
654 1 original_size = current_size;
655
1/2
✓ Branch 14 → 15 taken 1 time.
✗ Branch 14 → 18 not taken.
1 if (current_size < self->size_limit)
656 {
657 1 g_debug ("Cache doesn't need to be purged at %d bytes", current_size);
658 1 self->size_estimate = current_size;
659 1 g_task_return_boolean (task, FALSE);
660 1 return;
661 }
662
663 ✗ sqlite3_finalize (stmt);
664
665 /* Ok, delete the less popular tiles until size_limit reached */
666 ✗ query = "SELECT filename, size, popularity FROM tiles ORDER BY popularity";
667 ✗ rc = sqlite3_prepare (self->db, query, strlen (query), &stmt, NULL);
668 ✗ if (rc != SQLITE_OK)
669 {
670 ✗ g_warning ("Can't fetch tiles to delete: %s", sqlite3_errmsg (self->db));
671 }
672
673 ✗ rc = sqlite3_step (stmt);
674 ✗ while (rc == SQLITE_ROW && current_size > self->size_limit)
675 {
676 ✗ const char *filename;
677 ✗ guint size;
678
679 ✗ filename = (const char *) sqlite3_column_text (stmt, 0);
680 ✗ size = sqlite3_column_int (stmt, 1);
681 ✗ highest_popularity = sqlite3_column_int (stmt, 2);
682 ✗ g_debug ("Deleting %s of size %d", filename, size);
683
684 ✗ delete_tile (self, filename);
685
686 ✗ current_size -= size;
687
688 ✗ rc = sqlite3_step (stmt);
689 }
690
691 ✗ g_debug ("Cache size is now %d bytes (reduced by %d bytes)", current_size, original_size - current_size);
692 ✗ self->size_estimate = current_size;
693 ✗ self->have_size_estimate = TRUE;
694
695 ✗ query = sqlite3_mprintf ("UPDATE tiles SET popularity = popularity - %d",
696 highest_popularity);
697 ✗ sqlite3_exec (self->db, query, NULL, NULL, &error);
698 ✗ if (error != NULL)
699 {
700 ✗ g_warning ("Updating popularity failed: %s", error);
701 ✗ sqlite3_free (error);
702 }
703 ✗ sqlite3_free (query);
704
705 ✗ sqlite3_exec (self->db, "PRAGMA incremental_vacuum;", NULL, NULL, &error);
706
707 ✗ self->purge_in_progress = FALSE;
708 ✗ g_task_return_boolean (task, TRUE);
709 }
710
711 /**
712 * shumate_file_cache_purge_cache_async:
713 * @self: a #ShumateFileCache
714 * @cancellable: (nullable): a #GCancellable
715 * @callback: a #GAsyncReadyCallback to execute upon completion
716 * @user_data: closure data for @callback
717 *
718 * Removes less used tiles from the cache, if necessary, until it fits in
719 * the size limit.
720 */
721 void
722 1 shumate_file_cache_purge_cache_async (ShumateFileCache *self,
723 GCancellable *cancellable,
724 GAsyncReadyCallback callback,
725 gpointer user_data)
726 {
727 2 g_autoptr(GTask) task = NULL;
728
729
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 1 time.
1 g_return_if_fail (SHUMATE_IS_FILE_CACHE (self));
730
1/8
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 10 taken 1 time.
✗ Branch 7 → 8 not taken.
✗ Branch 7 → 9 not taken.
✗ Branch 8 → 9 not taken.
✗ Branch 8 → 10 not taken.
✗ Branch 9 → 10 not taken.
✗ Branch 9 → 14 not taken.
1 g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
731
732 1 task = g_task_new (self, cancellable, callback, user_data);
733
1/2
✓ Branch 13 → 15 taken 1 time.
✗ Branch 13 → 16 not taken.
1 g_task_set_source_tag (task, shumate_file_cache_purge_cache_async);
734
735
1/2
✗ Branch 16 → 17 not taken.
✓ Branch 16 → 19 taken 1 time.
1 if (self->purge_in_progress)
736 {
737 ✗ g_task_return_boolean (task, FALSE);
738 ✗ return;
739 }
740
741 1 self->purge_in_progress = TRUE;
742
1/2
✓ Branch 20 → 21 taken 1 time.
✗ Branch 20 → 23 not taken.
1 g_task_run_in_thread (task, purge_cache);
743 }
744
745 /**
746 * shumate_file_cache_purge_cache_finish:
747 * @self: a #ShumateFileCache
748 * @result: a #GAsyncResult provided to callback
749 * @error: a location for a #GError, or %NULL
750 *
751 * Gets the result of an async operation started using
752 * shumate_file_cache_purge_cache_async().
753 *
754 * Returns: %TRUE if any tiles were removed, otherwise %FALSE
755 */
756 gboolean
757 ✗ shumate_file_cache_purge_cache_finish (ShumateFileCache *self,
758 GAsyncResult *result,
759 GError **error)
760 {
761 ✗ g_return_val_if_fail (SHUMATE_IS_FILE_CACHE (self), FALSE);
762 ✗ g_return_val_if_fail (g_task_is_valid (result, self), FALSE);
763
764 ✗ return g_task_propagate_boolean (G_TASK (result), error);
765 }
766
767
768 typedef struct {
769 char *etag;
770 GDateTime *modtime;
771 } GetTileData;
772
773 static void
774 2 get_tile_data_free (GetTileData *data)
775 {
776
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 2 times.
2 g_clear_pointer (&data->etag, g_free);
777
1/2
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 2 times.
2 g_clear_pointer (&data->modtime, g_date_time_unref);
778
1/2
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 2 times.
2 g_free (data);
779 2 }
780
781 static void on_get_tile_file_loaded (GObject *source_object, GAsyncResult *res, gpointer user_data);
782
783
784 /**
785 * shumate_file_cache_get_tile_async:
786 * @self: a #ShumateFileCache
787 * @x: the X coordinate of the tile
788 * @y: the Y coordinate of the tile
789 * @zoom_level: the zoom level of the tile
790 * @cancellable: (nullable): a #GCancellable
791 * @callback: a #GAsyncReadyCallback to execute upon completion
792 * @user_data: closure data for @callback
793 *
794 * Gets tile data from the cache, if it is available.
795 */
796 void
797 2 shumate_file_cache_get_tile_async (ShumateFileCache *self,
798 int x,
799 int y,
800 int zoom_level,
801 GCancellable *cancellable,
802 GAsyncReadyCallback callback,
803 gpointer user_data)
804 {
805 2 g_autoptr(GTask) task = NULL;
806
2/4
✓ Branch 40 → 41 taken 1 time.
✗ Branch 40 → 42 not taken.
✓ Branch 50 → 51 taken 1 time.
✗ Branch 50 → 52 not taken.
2 g_autoptr(GFile) file = NULL;
807
2/4
✓ Branch 38 → 39 taken 1 time.
✗ Branch 38 → 40 not taken.
✓ Branch 48 → 49 taken 1 time.
✗ Branch 48 → 50 not taken.
2 g_autofree char *filename = NULL;
808 2 g_autoptr(GFileInfo) info = NULL;
809
2/4
✓ Branch 35 → 36 taken 1 time.
✗ Branch 35 → 37 not taken.
✗ Branch 45 → 46 not taken.
✓ Branch 45 → 47 taken 1 time.
2 g_autoptr(GError) error = NULL;
810 2 GetTileData *task_data = NULL;
811
812
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 2 times.
2 g_return_if_fail (SHUMATE_IS_FILE_CACHE (self));
813
1/8
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 10 taken 2 times.
✗ Branch 7 → 8 not taken.
✗ Branch 7 → 9 not taken.
✗ Branch 8 → 9 not taken.
✗ Branch 8 → 10 not taken.
✗ Branch 9 → 10 not taken.
✗ Branch 9 → 14 not taken.
2 g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
814
815 2 task = g_task_new (self, cancellable, callback, user_data);
816
1/2
✓ Branch 13 → 15 taken 2 times.
✗ Branch 13 → 16 not taken.
2 g_task_set_source_tag (task, shumate_file_cache_get_tile_async);
817
818 2 task_data = g_new0 (GetTileData, 1);
819 2 g_task_set_task_data (task, task_data, (GDestroyNotify) get_tile_data_free);
820
821 2 filename = get_filename (self, x, y, zoom_level);
822 2 file = g_file_new_for_path (filename);
823
824 /* Retrieve modification time */
825 2 info = g_file_query_info (file,
826 G_FILE_ATTRIBUTE_TIME_MODIFIED,
827 G_FILE_QUERY_INFO_NONE, cancellable, &error);
828
2/2
✓ Branch 21 → 22 taken 1 time.
✓ Branch 21 → 28 taken 1 time.
2 if (error)
829 {
830
1/2
✓ Branch 24 → 25 taken 1 time.
✗ Branch 24 → 26 not taken.
1 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
831 1 g_task_return_pointer (task, NULL, NULL);
832 else
833 ✗ g_task_return_error (task, g_error_copy (error));
834
835 return;
836 }
837
838 1 task_data->modtime = g_file_info_get_modification_date_time (info);
839 1 task_data->etag = db_get_etag (self, x, y, zoom_level);
840
841 /* update tile popularity */
842 1 on_tile_filled (self, x, y, zoom_level);
843
844
1/2
✗ Branch 33 → 34 not taken.
✓ Branch 33 → 35 taken 1 time.
1 g_file_load_bytes_async (file, cancellable, on_get_tile_file_loaded, g_object_ref (task));
845 }
846
847
848 static void
849 1 on_get_tile_file_loaded (GObject *source_object,
850 GAsyncResult *res,
851 gpointer user_data)
852 {
853 1 g_autoptr(GTask) task = user_data;
854 1 GFile *file = G_FILE (source_object);
855
1/4
✗ Branch 12 → 13 not taken.
✗ Branch 12 → 14 not taken.
✓ Branch 18 → 19 taken 1 time.
✗ Branch 18 → 20 not taken.
1 g_autoptr(GError) error = NULL;
856 1 GBytes *bytes;
857
858 1 bytes = g_file_load_bytes_finish (file, res, NULL, &error);
859
860
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 15 taken 1 time.
1 if (error != NULL)
861 {
862 /* Return NULL but not an error if the file doesn't exist */
863 ✗ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
864 ✗ g_task_return_pointer (task, NULL, NULL);
865 else
866 ✗ g_task_return_error (task, g_error_copy (error));
867
868 ✗ return;
869 }
870
871
1/2
✗ Branch 16 → 17 not taken.
✓ Branch 16 → 18 taken 1 time.
1 g_task_return_pointer (task, bytes, (GDestroyNotify) g_bytes_unref);
872 }
873
874
875 /**
876 * shumate_file_cache_get_tile_finish:
877 * @self: a #ShumateFileCache
878 * @etag: (nullable) (out) (optional): a location for the data's ETag, or %NULL
879 * @modtime: (nullable) (out) (optional): a location to return the tile's last modification time, or %NULL
880 * @result: a #GAsyncResult provided to callback
881 * @error: a location for a #GError, or %NULL
882 *
883 * Gets the tile data from a completed shumate_file_cache_get_tile_async()
884 * operation.
885 *
886 * @modtime will be set to the time the tile was added to the cache, or the
887 * latest time it was confirmed to be up to date.
888 *
889 * @etag will be set to the data's ETag, if present.
890 *
891 * Returns: a #GBytes containing the tile data, or %NULL if the tile was not in
892 * the cache or an error occurred
893 */
894 GBytes *
895 2 shumate_file_cache_get_tile_finish (ShumateFileCache *self,
896 char **etag,
897 GDateTime **modtime,
898 GAsyncResult *result,
899 GError **error)
900 {
901 2 GetTileData *data = g_task_get_task_data (G_TASK (result));
902
903
1/2
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 2 times.
2 g_return_val_if_fail (SHUMATE_IS_FILE_CACHE (self), NULL);
904
1/2
✓ Branch 7 → 8 taken 2 times.
✗ Branch 7 → 9 not taken.
2 g_return_val_if_fail (g_task_is_valid (result, self), NULL);
905
906
1/2
✓ Branch 8 → 10 taken 2 times.
✗ Branch 8 → 11 not taken.
2 if (etag)
907 2 *etag = g_steal_pointer (&data->etag);
908
1/2
✓ Branch 11 → 12 taken 2 times.
✗ Branch 11 → 13 not taken.
2 if (modtime)
909 2 *modtime = g_steal_pointer (&data->modtime);
910
911 2 return g_task_propagate_pointer (G_TASK (result), error);
912 }
913
914
915 typedef struct {
916 ShumateFileCache *self;
917 char *etag;
918 GBytes *bytes;
919 char *filename;
920 } StoreTileData;
921
922 static void
923 1 store_tile_data_free (StoreTileData *data)
924 {
925
1/2
✓ Branch 2 → 3 taken 1 time.
✗ Branch 2 → 4 not taken.
1 g_clear_object (&data->self);
926
1/2
✓ Branch 4 → 5 taken 1 time.
✗ Branch 4 → 6 not taken.
1 g_clear_pointer (&data->etag, g_free);
927
1/2
✓ Branch 6 → 7 taken 1 time.
✗ Branch 6 → 8 not taken.
1 g_clear_pointer (&data->bytes, g_bytes_unref);
928
1/2
✓ Branch 8 → 9 taken 1 time.
✗ Branch 8 → 10 not taken.
1 g_clear_pointer (&data->filename, g_free);
929
1/2
✗ Branch 10 → 11 not taken.
✓ Branch 10 → 12 taken 1 time.
1 g_free (data);
930 1 }
931 G_DEFINE_AUTOPTR_CLEANUP_FUNC (StoreTileData, store_tile_data_free);
932
933 static void on_file_created (GObject *object, GAsyncResult *result, gpointer user_data);
934 static void on_file_written (GObject *object, GAsyncResult *result, gpointer user_data);
935
936 /**
937 * shumate_file_cache_store_tile_async:
938 * @self: a #ShumateFileCache
939 * @x: the X coordinate of the tile
940 * @y: the Y coordinate of the tile
941 * @zoom_level: the zoom level of the tile
942 * @bytes: a #GBytes
943 * @etag: (nullable): an ETag string, or %NULL
944 * @cancellable: (nullable): a #GCancellable
945 * @callback: a #GAsyncReadyCallback to execute upon completion
946 * @user_data: closure data for @callback
947 *
948 * Stores a tile in the cache.
949 */
950 void
951 1 shumate_file_cache_store_tile_async (ShumateFileCache *self,
952 int x,
953 int y,
954 int zoom_level,
955 GBytes *bytes,
956 const char *etag,
957 GCancellable *cancellable,
958 GAsyncReadyCallback callback,
959 gpointer user_data)
960 {
961 1 g_autoptr(GTask) task = NULL;
962
1/4
✓ Branch 45 → 46 taken 1 time.
✗ Branch 45 → 47 not taken.
✗ Branch 53 → 54 not taken.
✗ Branch 53 → 55 not taken.
1 g_autofree char *filename = NULL;
963 1 g_autoptr(GFile) file = NULL;
964
1/4
✓ Branch 42 → 43 taken 1 time.
✗ Branch 42 → 44 not taken.
✗ Branch 50 → 51 not taken.
✗ Branch 50 → 52 not taken.
1 g_autofree char *path = NULL;
965 1 StoreTileData *data;
966
967
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 1 time.
1 g_return_if_fail (SHUMATE_IS_FILE_CACHE (self));
968
1/2
✓ Branch 5 → 6 taken 1 time.
✗ Branch 5 → 7 not taken.
1 g_return_if_fail (bytes != NULL);
969
1/8
✗ Branch 6 → 8 not taken.
✓ Branch 6 → 12 taken 1 time.
✗ Branch 9 → 10 not taken.
✗ Branch 9 → 11 not taken.
✗ Branch 10 → 11 not taken.
✗ Branch 10 → 12 not taken.
✗ Branch 11 → 12 not taken.
✗ Branch 11 → 16 not taken.
1 g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
970
971 1 task = g_task_new (self, cancellable, callback, user_data);
972
1/2
✓ Branch 15 → 17 taken 1 time.
✗ Branch 15 → 18 not taken.
1 g_task_set_source_tag (task, shumate_file_cache_store_tile_async);
973
974 1 filename = get_filename (self, x, y, zoom_level);
975 1 file = g_file_new_for_path (filename);
976
977 1 g_debug ("Update of tile (%d %d zoom %d)", x, y, zoom_level);
978
979 /* If needed, create the cache's dirs */
980 1 path = g_path_get_dirname (filename);
981
1/2
✗ Branch 23 → 24 not taken.
✓ Branch 23 → 27 taken 1 time.
1 if (g_mkdir_with_parents (path, 0700) == -1)
982 {
983 ✗ if (errno != EEXIST)
984 {
985 ✗ const char *error_string = g_strerror (errno);
986 ✗ g_task_return_new_error (task, SHUMATE_FILE_CACHE_ERROR,
987 SHUMATE_FILE_CACHE_ERROR_FAILED,
988 "Failed to create cache directory %s: %s", path, error_string);
989 ✗ return;
990 }
991 }
992
993 1 data = g_new0 (StoreTileData, 1);
994 1 data->self = g_object_ref (self);
995
1/2
✗ Branch 29 → 30 not taken.
✓ Branch 29 → 31 taken 1 time.
1 data->etag = g_strdup (etag);
996 1 data->bytes = g_bytes_ref (bytes);
997 1 data->filename = g_steal_pointer (&filename);
998 1 g_task_set_task_data (task, data, (GDestroyNotify) store_tile_data_free);
999
1000 1 g_file_replace_async (file, NULL, FALSE, G_FILE_CREATE_PRIVATE, G_PRIORITY_DEFAULT, cancellable, on_file_created, g_object_ref (task));
1001 }
1002
1003 static void
1004 1 on_file_created (GObject *object,
1005 GAsyncResult *res,
1006 gpointer user_data)
1007 {
1008 1 g_autoptr(GTask) task = user_data;
1009 1 StoreTileData *data = g_task_get_task_data (task);
1010 1 GCancellable *cancellable = g_task_get_cancellable (task);
1011 1 GError *error = NULL;
1012
1/4
✗ Branch 9 → 10 not taken.
✗ Branch 9 → 11 not taken.
✓ Branch 17 → 18 taken 1 time.
✗ Branch 17 → 19 not taken.
1 g_autoptr(GFileOutputStream) ostream = NULL;
1013 1 gconstpointer contents;
1014 1 gsize size;
1015
1016 1 ostream = g_file_create_finish (G_FILE (object), res, &error);
1017
1/2
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 12 taken 1 time.
1 if (error != NULL)
1018 {
1019 ✗ g_task_return_error (task, error);
1020 ✗ return;
1021 }
1022
1023 1 contents = g_bytes_get_data (data->bytes, &size);
1024
1025
1/2
✓ Branch 15 → 16 taken 1 time.
✗ Branch 15 → 17 not taken.
1 g_output_stream_write_all_async (G_OUTPUT_STREAM (ostream),
1026 contents, size, G_PRIORITY_DEFAULT,
1027 cancellable, on_file_written, g_object_ref (task));
1028 }
1029
1030 static void
1031 1 on_file_written (GObject *object,
1032 GAsyncResult *res,
1033 gpointer user_data)
1034 {
1035 1 g_autoptr(GTask) task = user_data;
1036 1 StoreTileData *data = g_task_get_task_data (task);
1037
1/4
✓ Branch 21 → 22 taken 1 time.
✗ Branch 21 → 23 not taken.
✗ Branch 29 → 30 not taken.
✗ Branch 29 → 31 not taken.
1 g_autoptr(sqlite_str) query = NULL;
1038
1/4
✓ Branch 19 → 20 taken 1 time.
✗ Branch 19 → 21 not taken.
✗ Branch 27 → 28 not taken.
✗ Branch 27 → 29 not taken.
1 g_autoptr(sqlite_str) sql_error = NULL;
1039 1 GError *error = NULL;
1040 1 guint tile_size = g_bytes_get_size (data->bytes);
1041
1042 1 g_output_stream_write_all_finish (G_OUTPUT_STREAM (object), res, NULL, &error);
1043
1/2
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 1 time.
1 if (error != NULL)
1044 {
1045 ✗ g_task_return_error (task, error);
1046 ✗ return;
1047 }
1048
1049 1 query = sqlite3_mprintf ("REPLACE INTO tiles (filename, etag, size) VALUES (%Q, %Q, %d)",
1050 data->filename, data->etag, tile_size);
1051 1 sqlite3_exec (data->self->db, query, NULL, NULL, &sql_error);
1052
1/2
✗ Branch 9 → 10 not taken.
✓ Branch 9 → 13 taken 1 time.
1 if (sql_error != NULL)
1053 {
1054 ✗ g_task_return_new_error (task, SHUMATE_FILE_CACHE_ERROR, SHUMATE_FILE_CACHE_ERROR_FAILED,
1055 "Failed to insert tile into SQLite database: %s", sql_error);
1056 ✗ return;
1057 }
1058
1059 1 data->self->size_estimate += tile_size;
1060
1/4
✗ Branch 13 → 14 not taken.
✓ Branch 13 → 15 taken 1 time.
✗ Branch 14 → 15 not taken.
✗ Branch 14 → 16 not taken.
1 if (!data->self->have_size_estimate || data->self->size_estimate > data->self->size_limit + 5000000)
1061 {
1062 /* automatically purge the cache if the size estimate is 5MB over
1063 * the limit, or if there is no estimate of the cache size yet */
1064
1065 1 shumate_file_cache_purge_cache_async (data->self, NULL, NULL, NULL);
1066 }
1067
1068
1069
1/2
✗ Branch 17 → 18 not taken.
✓ Branch 17 → 19 taken 1 time.
1 g_task_return_boolean (task, TRUE);
1070 }
1071
1072
1073 /**
1074 * shumate_file_cache_store_tile_finish:
1075 * @self: an #ShumateFileCache
1076 * @result: a #GAsyncResult provided to callback
1077 * @error: a location for a #GError, or %NULL
1078 *
1079 * Gets the success value of a completed shumate_file_cache_store_tile_async()
1080 * operation.
1081 *
1082 * Returns: %TRUE if the operation was successful, otherwise %FALSE
1083 */
1084 gboolean
1085 1 shumate_file_cache_store_tile_finish (ShumateFileCache *self,
1086 GAsyncResult *result,
1087 GError **error)
1088 {
1089
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 1 time.
1 g_return_val_if_fail (SHUMATE_IS_FILE_CACHE (self), FALSE);
1090
1/2
✓ Branch 6 → 7 taken 1 time.
✗ Branch 6 → 8 not taken.
1 g_return_val_if_fail (g_task_is_valid (result, self), FALSE);
1091
1092 1 return g_task_propagate_boolean (G_TASK (result), error);
1093 }
1094
1095 /**
1096 * shumate_file_cache_error_quark:
1097 *
1098 * Gets the #ShumateFileCache error quark.
1099 *
1100 * Returns: a #GQuark
1101 */
1102
1/2
✓ Branch 2 → 3 taken 1 time.
✗ Branch 2 → 5 not taken.
1 G_DEFINE_QUARK (shumate-file-cache-error-quark, shumate_file_cache_error);
1103