comparison src/filedata.c @ 1728:94ced97edf0b

improved the sidecar grouping algorithm
author nadvornik
date Fri, 28 Aug 2009 20:18:46 +0000
parents ea4effc8398c
children 672ee190869e
comparison
equal deleted inserted replaced
1727:68b791050958 1728:94ced97edf0b
23 #include "histogram.h" 23 #include "histogram.h"
24 24
25 25
26 static GHashTable *file_data_pool = NULL; 26 static GHashTable *file_data_pool = NULL;
27 static GHashTable *file_data_planned_change_hash = NULL; 27 static GHashTable *file_data_planned_change_hash = NULL;
28 static GHashTable *file_data_basename_hash = NULL;
29 28
30 static gint sidecar_file_priority(const gchar *path); 29 static gint sidecar_file_priority(const gchar *path);
31 static FileData *file_data_new_local(const gchar *path, struct stat *st, gboolean check_sidecars, gboolean stat_sidecars); 30 static FileData *file_data_new_local(const gchar *path, struct stat *st, gboolean check_sidecars, GHashTable *basename_hash);
32 31
33 32
34 /* 33 /*
35 *----------------------------------------------------------------------------- 34 *-----------------------------------------------------------------------------
36 * text conversion utils 35 * text conversion utils
136 * file info struct 135 * file info struct
137 *----------------------------------------------------------------------------- 136 *-----------------------------------------------------------------------------
138 */ 137 */
139 138
140 FileData *file_data_merge_sidecar_files(FileData *target, FileData *source); 139 FileData *file_data_merge_sidecar_files(FileData *target, FileData *source);
141 static void file_data_check_sidecars(FileData *fd, gboolean stat_sidecars); 140 static void file_data_check_sidecars(FileData *fd, GHashTable *basename_hash);
142 FileData *file_data_disconnect_sidecar_file(FileData *target, FileData *sfd); 141 FileData *file_data_disconnect_sidecar_file(FileData *target, FileData *sfd);
143 142
144 143
145 void file_data_increment_version(FileData *fd) 144 void file_data_increment_version(FileData *fd)
146 { 145 {
159 const FileData *fdb = b; 158 const FileData *fdb = b;
160 159
161 return strcmp(fdb->extension, fda->extension); 160 return strcmp(fdb->extension, fda->extension);
162 } 161 }
163 162
164 static void file_data_basename_hash_insert(FileData *fd) 163 static GHashTable *file_data_basename_hash_new(void)
164 {
165 return g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
166 }
167
168 static void file_data_basename_hash_insert(GHashTable *basename_hash, FileData *fd)
165 { 169 {
166 GList *list; 170 GList *list;
167 const gchar *ext = extension_from_path(fd->path); 171 const gchar *ext = extension_from_path(fd->path);
168 gchar *basename = ext ? g_strndup(fd->path, ext - fd->path) : g_strdup(fd->path); 172 gchar *basename = ext ? g_strndup(fd->path, ext - fd->path) : g_strdup(fd->path);
169 if (!file_data_basename_hash) 173
170 file_data_basename_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); 174 list = g_hash_table_lookup(basename_hash, basename);
171
172 list = g_hash_table_lookup(file_data_basename_hash, basename);
173 175
174 if (!g_list_find(list, fd)) 176 if (!g_list_find(list, fd))
175 { 177 {
176 list = g_list_insert_sorted(list, fd, file_data_sort_by_ext); 178 list = g_list_insert_sorted(list, file_data_ref(fd), file_data_sort_by_ext);
177 g_hash_table_insert(file_data_basename_hash, basename, list); 179 g_hash_table_insert(basename_hash, basename, list);
178 } 180 }
179 else 181 else
180 { 182 {
181 g_free(basename); 183 g_free(basename);
182 } 184 }
183 } 185 }
184 186
185 static void file_data_basename_hash_remove(FileData *fd) 187 static void file_data_basename_hash_remove(GHashTable *basename_hash, FileData *fd)
186 { 188 {
187 GList *list; 189 GList *list;
188 const gchar *ext = extension_from_path(fd->path); 190 const gchar *ext = extension_from_path(fd->path);
189 gchar *basename = ext ? g_strndup(fd->path, ext - fd->path) : g_strdup(fd->path); 191 gchar *basename = ext ? g_strndup(fd->path, ext - fd->path) : g_strdup(fd->path);
190 if (!file_data_basename_hash) 192
191 file_data_basename_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); 193 list = g_hash_table_lookup(basename_hash, basename);
192 194
193 list = g_hash_table_lookup(file_data_basename_hash, basename); 195 if (!g_list_find(list, fd)) return;
194 196
195 list = g_list_remove(list, fd); 197 list = g_list_remove(list, fd);
198 file_data_unref(fd);
196 199
197 if (list) 200 if (list)
198 { 201 {
199 g_hash_table_insert(file_data_basename_hash, basename, list); 202 g_hash_table_insert(basename_hash, basename, list);
200 } 203 }
201 else 204 else
202 { 205 {
203 g_hash_table_remove(file_data_basename_hash, basename); 206 g_hash_table_remove(basename_hash, basename);
204 g_free(basename); 207 g_free(basename);
205 } 208 }
209 }
210
211 static void file_data_basename_hash_remove_list(gpointer key, gpointer value, gpointer data)
212 {
213 filelist_free((GList *)value);
214 }
215
216 static void file_data_basename_hash_free(GHashTable *basename_hash)
217 {
218 g_hash_table_foreach(basename_hash, file_data_basename_hash_remove_list, NULL);
219 g_hash_table_destroy(basename_hash);
206 } 220 }
207 221
208 static void file_data_set_collate_keys(FileData *fd) 222 static void file_data_set_collate_keys(FileData *fd)
209 { 223 {
210 gchar *caseless_name; 224 gchar *caseless_name;
222 fd->collate_key_name_nocase = g_utf8_collate_key(caseless_name, -1); 236 fd->collate_key_name_nocase = g_utf8_collate_key(caseless_name, -1);
223 #endif 237 #endif
224 g_free(caseless_name); 238 g_free(caseless_name);
225 } 239 }
226 240
227 static void file_data_set_path(FileData *fd, const gchar *path) 241 static void file_data_set_path(FileData *fd, const gchar *path, GHashTable *basename_hash)
228 { 242 {
229 g_assert(path /* && *path*/); /* view_dir_tree uses FileData with zero length path */ 243 g_assert(path /* && *path*/); /* view_dir_tree uses FileData with zero length path */
230 g_assert(file_data_pool); 244 g_assert(file_data_pool);
231 245
232 if (fd->path) file_data_basename_hash_remove(fd); 246 if (basename_hash && fd->path) file_data_basename_hash_remove(basename_hash, fd);
233 247
234 g_free(fd->path); 248 g_free(fd->path);
235 249
236 if (fd->original_path) 250 if (fd->original_path)
237 { 251 {
281 if (fd->extension == NULL) 295 if (fd->extension == NULL)
282 { 296 {
283 fd->extension = fd->name + strlen(fd->name); 297 fd->extension = fd->name + strlen(fd->name);
284 } 298 }
285 299
286 file_data_basename_hash_insert(fd); /* we can ignore the special cases above - they don't have extensions */ 300 if (basename_hash) file_data_basename_hash_insert(basename_hash, fd); /* we can ignore the special cases above - they don't have extensions */
287 301
288 file_data_set_collate_keys(fd); 302 file_data_set_collate_keys(fd);
289 } 303 }
290 304
291 static gboolean file_data_check_changed_files_recursive(FileData *fd, struct stat *st) 305 static gboolean file_data_check_changed_files_recursive(FileData *fd, struct stat *st)
362 } 376 }
363 377
364 return ret; 378 return ret;
365 } 379 }
366 380
367 static FileData *file_data_new(const gchar *path_utf8, struct stat *st, gboolean check_sidecars, gboolean stat_sidecars) 381 static FileData *file_data_new(const gchar *path_utf8, struct stat *st, gboolean check_sidecars, GHashTable *basename_hash)
368 { 382 {
369 FileData *fd; 383 FileData *fd;
370 384
371 DEBUG_2("file_data_new: '%s' %d %d", path_utf8, check_sidecars, stat_sidecars); 385 DEBUG_2("file_data_new: '%s' %d %d", path_utf8, check_sidecars, !!basename_hash);
372 386
373 if (!file_data_pool) 387 if (!file_data_pool)
374 file_data_pool = g_hash_table_new(g_str_hash, g_str_equal); 388 file_data_pool = g_hash_table_new(g_str_hash, g_str_equal);
375 389
376 fd = g_hash_table_lookup(file_data_pool, path_utf8); 390 fd = g_hash_table_lookup(file_data_pool, path_utf8);
397 if (fd->parent) 411 if (fd->parent)
398 changed = file_data_check_changed_files(fd); 412 changed = file_data_check_changed_files(fd);
399 else 413 else
400 changed = file_data_check_changed_files_recursive(fd, st); 414 changed = file_data_check_changed_files_recursive(fd, st);
401 if (changed && check_sidecars && sidecar_file_priority(fd->extension)) 415 if (changed && check_sidecars && sidecar_file_priority(fd->extension))
402 file_data_check_sidecars(fd, stat_sidecars); 416 file_data_check_sidecars(fd, basename_hash);
403 DEBUG_2("file_data_pool hit: '%s' %s", fd->path, changed ? "(changed)" : ""); 417 DEBUG_2("file_data_pool hit: '%s' %s", fd->path, changed ? "(changed)" : "");
404 418
405 return fd; 419 return fd;
406 } 420 }
407 421
411 fd->date = st->st_mtime; 425 fd->date = st->st_mtime;
412 fd->mode = st->st_mode; 426 fd->mode = st->st_mode;
413 fd->ref = 1; 427 fd->ref = 1;
414 fd->magick = 0x12345678; 428 fd->magick = 0x12345678;
415 429
416 file_data_set_path(fd, path_utf8); /* set path, name, collate_key_*, original_path */ 430 file_data_set_path(fd, path_utf8, basename_hash); /* set path, name, collate_key_*, original_path */
417 431
418 if (check_sidecars) 432 if (check_sidecars)
419 file_data_check_sidecars(fd, stat_sidecars); 433 file_data_check_sidecars(fd, basename_hash);
420 434
421 return fd; 435 return fd;
422 } 436 }
423 437
424 /* extension must contain only ASCII characters */ 438 /* extension must contain only ASCII characters */
457 g_free(sl); 471 g_free(sl);
458 472
459 return list; 473 return list;
460 } 474 }
461 475
462 static void file_data_check_sidecars(FileData *fd, gboolean stat_sidecars) 476 static void file_data_check_sidecars(FileData *fd, GHashTable *basename_hash)
463 { 477 {
464 gint base_len; 478 gint base_len;
465 GString *fname; 479 GString *fname;
466 FileData *parent_fd = NULL; 480 FileData *parent_fd = NULL;
467 GList *work; 481 GList *work;
471 return; 485 return;
472 486
473 base_len = fd->extension - fd->path; 487 base_len = fd->extension - fd->path;
474 fname = g_string_new_len(fd->path, base_len); 488 fname = g_string_new_len(fd->path, base_len);
475 489
476 if (!stat_sidecars) 490 if (basename_hash)
477 { 491 {
478 basename_list = g_hash_table_lookup(file_data_basename_hash, fname->str); 492 basename_list = g_hash_table_lookup(basename_hash, fname->str);
479 } 493 }
480 494
481 495
482 /* check for possible sidecar files; 496 /* check for possible sidecar files;
483 the sidecar files created here are referenced only via fd->sidecar_files or fd->parent, 497 the sidecar files created here are referenced only via fd->sidecar_files or fd->parent,
493 while (work) 507 while (work)
494 { 508 {
495 gchar *ext = work->data; 509 gchar *ext = work->data;
496 work = work->next; 510 work = work->next;
497 511
498 if (stat_sidecars) 512 if (!basename_hash)
499 { 513 {
500 GList *new_list; 514 GList *new_list;
501 g_string_truncate(fname, base_len); 515 g_string_truncate(fname, base_len);
502 g_string_append(fname, ext); 516 g_string_append(fname, ext);
503 new_list = check_case_insensitive_ext(fname->str); 517 new_list = check_case_insensitive_ext(fname->str);
507 { 521 {
508 const GList *work2 = basename_list; 522 const GList *work2 = basename_list;
509 523
510 while (work2) 524 while (work2)
511 { 525 {
512 struct stat nst;
513 FileData *sfd = work2->data; 526 FileData *sfd = work2->data;
514 527
515 if (g_ascii_strcasecmp(ext, sfd->extension) == 0 && 528 if (g_ascii_strcasecmp(ext, sfd->extension) == 0)
516 (sfd == fd || stat_utf8(sfd->path, &nst)))
517 /* basename list can contain deleted files, fd was recently stat'd by caller */
518 { 529 {
519 group_list = g_list_append(group_list, file_data_ref(sfd)); 530 group_list = g_list_append(group_list, file_data_ref(sfd));
520 } 531 }
521 work2 = work2->next; 532 work2 = work2->next;
522 } 533 }
546 } 557 }
547 g_list_free(group_list); 558 g_list_free(group_list);
548 } 559 }
549 560
550 561
551 static FileData *file_data_new_local(const gchar *path, struct stat *st, gboolean check_sidecars, gboolean stat_sidecars) 562 static FileData *file_data_new_local(const gchar *path, struct stat *st, gboolean check_sidecars, GHashTable *basename_hash)
552 { 563 {
553 gchar *path_utf8 = path_to_utf8(path); 564 gchar *path_utf8 = path_to_utf8(path);
554 FileData *ret = file_data_new(path_utf8, st, check_sidecars, stat_sidecars); 565 FileData *ret = file_data_new(path_utf8, st, check_sidecars, basename_hash);
555 566
556 g_free(path_utf8); 567 g_free(path_utf8);
557 return ret; 568 return ret;
558 } 569 }
559 570
565 { 576 {
566 st.st_size = 0; 577 st.st_size = 0;
567 st.st_mtime = 0; 578 st.st_mtime = 0;
568 } 579 }
569 580
570 return file_data_new(path_utf8, &st, TRUE, TRUE); 581 return file_data_new(path_utf8, &st, TRUE, NULL);
571 } 582 }
572 583
573 FileData *file_data_add_sidecar_file(FileData *target, FileData *sfd) 584 FileData *file_data_add_sidecar_file(FileData *target, FileData *sfd)
574 { 585 {
575 sfd->parent = target; 586 sfd->parent = target;
627 static void file_data_free(FileData *fd) 638 static void file_data_free(FileData *fd)
628 { 639 {
629 g_assert(fd->magick == 0x12345678); 640 g_assert(fd->magick == 0x12345678);
630 g_assert(fd->ref == 0); 641 g_assert(fd->ref == 0);
631 642
632 if (fd->path) file_data_basename_hash_remove(fd);
633 g_hash_table_remove(file_data_pool, fd->original_path); 643 g_hash_table_remove(file_data_pool, fd->original_path);
634 644
635 g_free(fd->path); 645 g_free(fd->path);
636 g_free(fd->original_path); 646 g_free(fd->original_path);
637 g_free(fd->collate_key_name); 647 g_free(fd->collate_key_name);
989 struct dirent *dir; 999 struct dirent *dir;
990 gchar *pathl; 1000 gchar *pathl;
991 GList *dlist = NULL; 1001 GList *dlist = NULL;
992 GList *flist = NULL; 1002 GList *flist = NULL;
993 gint (*stat_func)(const gchar *path, struct stat *buf); 1003 gint (*stat_func)(const gchar *path, struct stat *buf);
1004 GHashTable *basename_hash = NULL;
994 1005
995 g_assert(files || dirs); 1006 g_assert(files || dirs);
996 1007
997 if (files) *files = NULL; 1008 if (files) *files = NULL;
998 if (dirs) *dirs = NULL; 1009 if (dirs) *dirs = NULL;
1004 if (dp == NULL) 1015 if (dp == NULL)
1005 { 1016 {
1006 g_free(pathl); 1017 g_free(pathl);
1007 return FALSE; 1018 return FALSE;
1008 } 1019 }
1020
1021 if (files) basename_hash = file_data_basename_hash_new();
1009 1022
1010 if (follow_symlinks) 1023 if (follow_symlinks)
1011 stat_func = stat; 1024 stat_func = stat;
1012 else 1025 else
1013 stat_func = lstat; 1026 stat_func = lstat;
1031 !(name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0'))) && 1044 !(name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0'))) &&
1032 strcmp(name, GQ_CACHE_LOCAL_THUMB) != 0 && 1045 strcmp(name, GQ_CACHE_LOCAL_THUMB) != 0 &&
1033 strcmp(name, GQ_CACHE_LOCAL_METADATA) != 0 && 1046 strcmp(name, GQ_CACHE_LOCAL_METADATA) != 0 &&
1034 strcmp(name, THUMB_FOLDER_LOCAL) != 0) 1047 strcmp(name, THUMB_FOLDER_LOCAL) != 0)
1035 { 1048 {
1036 dlist = g_list_prepend(dlist, file_data_new_local(filepath, &ent_sbuf, FALSE, FALSE)); 1049 dlist = g_list_prepend(dlist, file_data_new_local(filepath, &ent_sbuf, FALSE, NULL));
1037 } 1050 }
1038 } 1051 }
1039 else 1052 else
1040 { 1053 {
1041 if (files && filter_name_exists(name)) 1054 if (files && filter_name_exists(name))
1042 { 1055 {
1043 flist = g_list_prepend(flist, file_data_new_local(filepath, &ent_sbuf, TRUE, FALSE)); 1056 flist = g_list_prepend(flist, file_data_new_local(filepath, &ent_sbuf, TRUE, basename_hash));
1044 } 1057 }
1045 } 1058 }
1046 } 1059 }
1047 g_free(filepath); 1060 g_free(filepath);
1048 } 1061 }
1049 1062
1050 closedir(dp); 1063 closedir(dp);
1051 1064
1052 g_free(pathl); 1065 g_free(pathl);
1066 if (basename_hash) file_data_basename_hash_free(basename_hash);
1053 1067
1054 if (dirs) *dirs = dlist; 1068 if (dirs) *dirs = dlist;
1055 if (files) *files = filelist_filter_out_sidecars(flist); 1069 if (files) *files = filelist_filter_out_sidecars(flist);
1056 1070
1057 return TRUE; 1071 return TRUE;
2382 */ 2396 */
2383 DEBUG_1("can't rename fd, target exists %s -> %s", fd->change->dest, fd->path); 2397 DEBUG_1("can't rename fd, target exists %s -> %s", fd->change->dest, fd->path);
2384 } 2398 }
2385 else 2399 else
2386 { 2400 {
2387 file_data_set_path(fd, fd->change->dest); 2401 file_data_set_path(fd, fd->change->dest, NULL);
2388 } 2402 }
2389 } 2403 }
2390 file_data_increment_version(fd); 2404 file_data_increment_version(fd);
2391 file_data_send_notification(fd, NOTIFY_CHANGE); 2405 file_data_send_notification(fd, NOTIFY_CHANGE);
2392 2406