1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2005 by Miika Pekkarinen
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
23 * ----------x---------x------------------x-----
25 * +---------------x-------+ | TagCache | Libraries
26 * | Modification routines | | Core |
27 * +-x---------x-----------+ | |
29 * | +------x-------------x-+ +-------------x-----+ |
30 * | | x==x Filters & clauses | |
31 * | | Search routines | +-------------------+ |
32 * | | x============================x DirCache
33 * | +-x--------------------+ | (optional)
35 * | | +-------------------------------+ +---------+ |
36 * | | | DB Commit (sort,unique,index) | | | |
37 * | | +-x--------------------------x--+ | Control | |
38 * | | | (R/W) | (R) | Thread | |
39 * | | | +----------------------+ | | | |
40 * | | | | TagCache DB Builder | | +---------+ |
41 * | | | +-x-------------x------+ | |
42 * | | | | (R) | (W) | |
43 * | | | | +--x--------x---------+ |
44 * | | | | | Temporary Commit DB | |
45 * | | | | +---------------------+ |
46 * +-x----x-------x--+ |
47 * | TagCache RAM DB x==\(W) +-----------------+ |
48 * +-----------------+ \===x | |
49 * | | | | (R) | Ram DB Loader x============x DirCache
50 * +-x----x---x---x---+ /==x | | (optional)
51 * | Tagcache Disk DB x==/ +-----------------+ |
52 * +------------------+ |
74 #include "eeprom_settings.h"
77 /* Tag Cache thread. */
78 static struct event_queue tagcache_queue
;
79 static long tagcache_stack
[(DEFAULT_STACK_SIZE
+ 0x4000)/sizeof(long)];
80 static const char tagcache_thread_name
[] = "tagcache";
82 /* Previous path when scanning directory tree recursively. */
83 static char curpath
[MAX_PATH
*2];
84 static long curpath_size
= sizeof(curpath
);
86 /* Used when removing duplicates. */
87 static char *tempbuf
; /* Allocated when needed. */
88 static long tempbufidx
; /* Current location in buffer. */
89 static long tempbuf_size
; /* Buffer size (TEMPBUF_SIZE). */
90 static long tempbuf_left
; /* Buffer space left. */
91 static long tempbuf_pos
;
93 /* Tags we want to get sorted (loaded to the tempbuf). */
94 static const int sorted_tags
[] = { tag_artist
, tag_album
, tag_genre
, tag_composer
, tag_title
};
96 /* Uniqued tags (we can use these tags with filters and conditional clauses). */
97 static const int unique_tags
[] = { tag_artist
, tag_album
, tag_genre
, tag_composer
};
99 /* Numeric tags (we can use these tags with conditional clauses). */
100 static const int numeric_tags
[] = { tag_year
, tag_tracknumber
, tag_length
, tag_bitrate
,
101 tag_playcount
, tag_playtime
, tag_lastplayed
, tag_virt_autoscore
};
103 static const char *tags_str
[] = { "artist", "album", "genre", "title",
104 "filename", "composer", "year", "tracknumber", "bitrate", "length",
105 "playcount", "playtime", "lastplayed" };
107 /* Status information of the tagcache. */
108 static struct tagcache_stat stat
;
110 /* Queue commands. */
111 enum tagcache_queue
{
120 /* Tag database structures. */
122 /* Variable-length tag entry in tag files. */
123 struct tagfile_entry
{
124 short tag_length
; /* Length of the data in bytes including '\0' */
125 short idx_id
; /* Corresponding entry location in index file of not unique tags */
126 char tag_data
[0]; /* Begin of the tag data */
129 /* Fixed-size tag entry in master db index. */
131 long tag_seek
[TAG_COUNT
]; /* Location of tag data or numeric tag data */
132 long flag
; /* Status flags */
135 /* Header is the same in every file. */
136 struct tagcache_header
{
137 long magic
; /* Header version number */
138 long datasize
; /* Data size in bytes */
139 long entry_count
; /* Number of entries in this file */
142 struct master_header
{
143 struct tagcache_header tch
;
144 long serial
; /* Increasing counting number */
147 static long current_serial
;
149 #ifdef HAVE_TC_RAMCACHE
150 /* Header is created when loading database to ram. */
151 struct ramcache_header
{
152 struct master_header h
; /* Header from the master index */
153 struct index_entry
*indices
; /* Master index file content */
154 char *tags
[TAG_COUNT
]; /* Tag file content (not including filename tag) */
155 int entry_count
[TAG_COUNT
]; /* Number of entries in the indices. */
158 # ifdef HAVE_EEPROM_SETTINGS
159 struct statefile_header
{
160 struct ramcache_header
*hdr
;
161 struct tagcache_stat stat
;
165 /* Pointer to allocated ramcache_header */
166 static struct ramcache_header
*hdr
;
170 * Full tag entries stored in a temporary file waiting
171 * for commit to the cache. */
172 struct temp_file_entry
{
173 long tag_offset
[TAG_COUNT
];
174 short tag_length
[TAG_COUNT
];
180 struct tempbuf_id_list
{
182 struct tempbuf_id_list
*next
;
185 struct tempbuf_searchidx
{
189 struct tempbuf_id_list idlist
;
192 /* Lookup buffer for fixing messed up index while after sorting. */
193 static long commit_entry_count
;
194 static long lookup_buffer_depth
;
195 struct tempbuf_searchidx
**lookup
;
197 /* Used when building the temporary file. */
198 static int cachefd
= -1, filenametag_fd
;
199 static int total_entry_count
= 0;
200 static int data_size
= 0;
201 static int processed_dir_count
;
203 /* Thread safe locking */
204 static volatile int write_lock
;
205 static volatile int read_lock
;
207 int tagcache_str_to_tag(const char *str
)
211 for (i
= 0; i
< (long)(sizeof(tags_str
)/sizeof(tags_str
[0])); i
++)
213 if (!strcasecmp(tags_str
[i
], str
))
220 const char* tagcache_tag_to_str(int tag
)
222 return tags_str
[tag
];
225 bool tagcache_is_numeric_tag(int type
)
229 for (i
= 0; i
< (int)(sizeof(numeric_tags
)/sizeof(numeric_tags
[0])); i
++)
231 if (type
== numeric_tags
[i
])
238 bool tagcache_is_unique_tag(int type
)
242 for (i
= 0; i
< (int)(sizeof(unique_tags
)/sizeof(unique_tags
[0])); i
++)
244 if (type
== unique_tags
[i
])
251 bool tagcache_is_sorted_tag(int type
)
255 for (i
= 0; i
< (int)(sizeof(sorted_tags
)/sizeof(sorted_tags
[0])); i
++)
257 if (type
== sorted_tags
[i
])
264 static int open_tag_fd(struct tagcache_header
*hdr
, int tag
, bool write
)
270 if (tagcache_is_numeric_tag(tag
) || tag
< 0 || tag
>= TAG_COUNT
)
273 snprintf(buf
, sizeof buf
, TAGCACHE_FILE_INDEX
, tag
);
275 fd
= open(buf
, write
? O_RDWR
: O_RDONLY
);
278 logf("tag file open failed: %d", tag
);
283 /* Check the header. */
284 rc
= read(fd
, hdr
, sizeof(struct tagcache_header
));
285 if (hdr
->magic
!= TAGCACHE_MAGIC
|| rc
!= sizeof(struct tagcache_header
))
287 logf("header error");
296 #if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE)
297 static long find_entry_ram(const char *filename
,
298 const struct dircache_entry
*dc
)
300 static long last_pos
= 0;
304 /* Check if we tagcache is loaded into ram. */
309 dc
= dircache_get_entry_ptr(filename
);
313 logf("tagcache: file not found.");
324 for (; i
< hdr
->h
.tch
.entry_count
; i
++)
326 if (hdr
->indices
[i
].tag_seek
[tag_filename
] == (long)dc
)
328 last_pos
= MAX(0, i
- 3);
332 if (++counter
== 100)
349 static long find_entry_disk(const char *filename
)
351 struct tagcache_header tch
;
352 static long last_pos
= -1;
353 long pos_history
[POS_HISTORY_COUNT
];
354 long pos_history_idx
= 0;
356 struct tagfile_entry tfe
;
369 if ( (fd
= open_tag_fd(&tch
, tag_filename
, false)) < 0)
376 lseek(fd
, last_pos
, SEEK_SET
);
378 lseek(fd
, sizeof(struct tagcache_header
), SEEK_SET
);
382 pos
= lseek(fd
, 0, SEEK_CUR
);
383 for (i
= pos_history_idx
-1; i
>= 0; i
--)
384 pos_history
[i
+1] = pos_history
[i
];
385 pos_history
[0] = pos
;
387 if (read(fd
, &tfe
, sizeof(struct tagfile_entry
)) !=
388 sizeof(struct tagfile_entry
))
393 if (tfe
.tag_length
>= (long)sizeof(buf
))
395 logf("too long tag #1");
401 if (read(fd
, buf
, tfe
.tag_length
) != tfe
.tag_length
)
403 logf("read error #2");
409 if (!strcasecmp(filename
, buf
))
411 last_pos
= pos_history
[pos_history_idx
];
416 if (pos_history_idx
< POS_HISTORY_COUNT
- 1)
430 if (fd
!= filenametag_fd
)
435 if (fd
!= filenametag_fd
)
441 static int find_index(const char *filename
)
445 #if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE)
446 if (stat
.ramcache
&& dircache_is_enabled())
447 idx_id
= find_entry_ram(filename
, NULL
);
451 idx_id
= find_entry_disk(filename
);
456 bool tagcache_find_index(struct tagcache_search
*tcs
, const char *filename
)
463 idx_id
= find_index(filename
);
467 if (!tagcache_search(tcs
, tag_filename
))
470 tcs
->entry_count
= 0;
471 tcs
->idx_id
= idx_id
;
476 static bool get_index(int masterfd
, int idxid
,
477 struct index_entry
*idx
, bool use_ram
)
481 logf("Incorrect idxid: %d", idxid
);
485 #ifdef HAVE_TC_RAMCACHE
486 if (stat
.ramcache
&& use_ram
)
488 if (hdr
->indices
[idxid
].flag
& FLAG_DELETED
)
491 memcpy(idx
, &hdr
->indices
[idxid
], sizeof(struct index_entry
));
498 lseek(masterfd
, idxid
* sizeof(struct index_entry
)
499 + sizeof(struct master_header
), SEEK_SET
);
500 if (read(masterfd
, idx
, sizeof(struct index_entry
)) !=
501 sizeof(struct index_entry
))
503 logf("read error #3");
507 if (idx
->flag
& FLAG_DELETED
)
513 static bool write_index(int masterfd
, int idxid
, struct index_entry
*idx
)
515 /* We need to exclude all memory only flags & tags when writing to disk. */
516 if (idx
->flag
& FLAG_DIRCACHE
)
518 logf("memory only flags!");
522 #ifdef HAVE_TC_RAMCACHE
525 memcpy(&hdr
->indices
[idxid
], idx
, sizeof(struct index_entry
));
529 lseek(masterfd
, idxid
* sizeof(struct index_entry
)
530 + sizeof(struct master_header
), SEEK_SET
);
531 if (write(masterfd
, idx
, sizeof(struct index_entry
)) !=
532 sizeof(struct index_entry
))
534 logf("write error #3");
535 logf("idxid: %d", idxid
);
542 static bool open_files(struct tagcache_search
*tcs
, int tag
)
544 if (tcs
->idxfd
[tag
] < 0)
548 snprintf(fn
, sizeof fn
, TAGCACHE_FILE_INDEX
, tag
);
549 tcs
->idxfd
[tag
] = open(fn
, O_RDONLY
);
552 if (tcs
->idxfd
[tag
] < 0)
554 logf("File not open!");
561 static bool retrieve(struct tagcache_search
*tcs
, struct index_entry
*idx
,
562 int tag
, char *buf
, long size
)
564 struct tagfile_entry tfe
;
569 if (tagcache_is_numeric_tag(tag
))
572 seek
= idx
->tag_seek
[tag
];
575 logf("Retrieve failed");
579 #ifdef HAVE_TC_RAMCACHE
582 struct tagfile_entry
*ep
;
584 # ifdef HAVE_DIRCACHE
585 if (tag
== tag_filename
&& idx
->flag
& FLAG_DIRCACHE
)
587 dircache_copy_path((struct dircache_entry
*)seek
,
593 if (tag
!= tag_filename
)
595 ep
= (struct tagfile_entry
*)&hdr
->tags
[tag
][seek
];
596 strncpy(buf
, ep
->tag_data
, size
-1);
603 if (!open_files(tcs
, tag
))
606 lseek(tcs
->idxfd
[tag
], seek
, SEEK_SET
);
607 if (read(tcs
->idxfd
[tag
], &tfe
, sizeof(struct tagfile_entry
)) !=
608 sizeof(struct tagfile_entry
))
610 logf("read error #5");
614 if (tfe
.tag_length
>= size
)
616 logf("too small buffer");
620 if (read(tcs
->idxfd
[tag
], buf
, tfe
.tag_length
) !=
623 logf("read error #6");
627 buf
[tfe
.tag_length
] = '\0';
632 static long check_virtual_tags(int tag
, const struct index_entry
*idx
)
638 case tag_virt_autoscore
:
639 if (idx
->tag_seek
[tag_length
] == 0
640 || idx
->tag_seek
[tag_playcount
] == 0)
646 data
= 100 * idx
->tag_seek
[tag_playtime
]
647 / idx
->tag_seek
[tag_length
]
648 / idx
->tag_seek
[tag_playcount
];
653 data
= idx
->tag_seek
[tag
];
659 long tagcache_get_numeric(const struct tagcache_search
*tcs
, int tag
)
661 struct index_entry idx
;
666 if (!tagcache_is_numeric_tag(tag
))
669 if (!get_index(tcs
->masterfd
, tcs
->idx_id
, &idx
, true))
672 return check_virtual_tags(tag
, &idx
);
675 inline static bool str_ends_with(const char *str1
, const char *str2
)
677 int str_len
= strlen(str1
);
678 int clause_len
= strlen(str2
);
680 if (clause_len
> str_len
)
683 return !strcasecmp(&str1
[str_len
- clause_len
], str2
);
686 inline static bool str_oneof(const char *str
, const char *list
)
689 int l
, len
= strlen(str
);
693 sep
= strchr(list
, '|');
694 l
= sep
? (long)sep
- (long)list
: (int)strlen(list
);
695 if ((l
==len
) && !strncasecmp(str
, list
, len
))
697 list
+= sep
? l
+ 1 : l
;
703 static bool check_against_clause(long numeric
, const char *str
,
704 const struct tagcache_search_clause
*clause
)
708 switch (clause
->type
)
711 return numeric
== clause
->numeric_data
;
713 return numeric
!= clause
->numeric_data
;
715 return numeric
> clause
->numeric_data
;
717 return numeric
>= clause
->numeric_data
;
719 return numeric
< clause
->numeric_data
;
721 return numeric
<= clause
->numeric_data
;
723 logf("Incorrect numeric tag: %d", clause
->type
);
728 switch (clause
->type
)
731 return !strcasecmp(clause
->str
, str
);
733 return strcasecmp(clause
->str
, str
);
735 return 0>strcasecmp(clause
->str
, str
);
737 return 0>=strcasecmp(clause
->str
, str
);
739 return 0<strcasecmp(clause
->str
, str
);
741 return 0<=strcasecmp(clause
->str
, str
);
742 case clause_contains
:
743 return (strcasestr(str
, clause
->str
) != NULL
);
744 case clause_not_contains
:
745 return (strcasestr(str
, clause
->str
) == NULL
);
746 case clause_begins_with
:
747 return (strcasestr(str
, clause
->str
) == str
);
748 case clause_not_begins_with
:
749 return (strcasestr(str
, clause
->str
) != str
);
750 case clause_ends_with
:
751 return str_ends_with(str
, clause
->str
);
752 case clause_not_ends_with
:
753 return !str_ends_with(str
, clause
->str
);
755 return str_oneof(str
, clause
->str
);
758 logf("Incorrect tag: %d", clause
->type
);
765 bool check_clauses(struct tagcache_search
*tcs
,
766 struct index_entry
*idx
,
767 struct tagcache_search_clause
**clause
, int count
)
771 #ifdef HAVE_TC_RAMCACHE
774 /* Go through all conditional clauses. */
775 for (i
= 0; i
< count
; i
++)
777 struct tagfile_entry
*tfe
;
782 seek
= check_virtual_tags(clause
[i
]->tag
, idx
);
784 if (!tagcache_is_numeric_tag(clause
[i
]->tag
))
786 if (clause
[i
]->tag
== tag_filename
)
788 retrieve(tcs
, idx
, tag_filename
, buf
, sizeof buf
);
793 tfe
= (struct tagfile_entry
*)&hdr
->tags
[clause
[i
]->tag
][seek
];
798 if (!check_against_clause(seek
, str
, clause
[i
]))
805 /* Check for conditions. */
806 for (i
= 0; i
< count
; i
++)
808 struct tagfile_entry tfe
;
812 seek
= check_virtual_tags(clause
[i
]->tag
, idx
);
814 memset(str
, 0, sizeof str
);
815 if (!tagcache_is_numeric_tag(clause
[i
]->tag
))
817 int fd
= tcs
->idxfd
[clause
[i
]->tag
];
818 lseek(fd
, seek
, SEEK_SET
);
819 read(fd
, &tfe
, sizeof(struct tagfile_entry
));
820 if (tfe
.tag_length
>= (int)sizeof(str
))
822 logf("Too long tag read!");
826 read(fd
, str
, tfe
.tag_length
);
828 /* Check if entry has been deleted. */
833 if (!check_against_clause(seek
, str
, clause
[i
]))
841 bool tagcache_check_clauses(struct tagcache_search
*tcs
,
842 struct tagcache_search_clause
**clause
, int count
)
844 struct index_entry idx
;
849 if (!get_index(tcs
->masterfd
, tcs
->idx_id
, &idx
, true))
852 return check_clauses(tcs
, &idx
, clause
, count
);
855 static bool add_uniqbuf(struct tagcache_search
*tcs
, long id
)
859 /* If uniq buffer is not defined we must return true for search to work. */
860 if (tcs
->unique_list
== NULL
861 || (!tagcache_is_unique_tag(tcs
->type
)
862 && !tagcache_is_numeric_tag(tcs
->type
)))
867 for (i
= 0; i
< tcs
->unique_list_count
; i
++)
869 /* Return false if entry is found. */
870 if (tcs
->unique_list
[i
] == id
)
874 if (tcs
->unique_list_count
< tcs
->unique_list_capacity
)
876 tcs
->unique_list
[i
] = id
;
877 tcs
->unique_list_count
++;
883 static bool build_lookup_list(struct tagcache_search
*tcs
)
885 struct index_entry entry
;
888 tcs
->seek_list_count
= 0;
890 #ifdef HAVE_TC_RAMCACHE
895 for (i
= tcs
->seek_pos
; i
< hdr
->h
.tch
.entry_count
; i
++)
897 struct index_entry
*idx
= &hdr
->indices
[i
];
898 if (tcs
->seek_list_count
== SEEK_LIST_SIZE
)
901 /* Skip deleted files. */
902 if (idx
->flag
& FLAG_DELETED
)
905 /* Go through all filters.. */
906 for (j
= 0; j
< tcs
->filter_count
; j
++)
908 if (idx
->tag_seek
[tcs
->filter_tag
[j
]] != tcs
->filter_seek
[j
])
914 if (j
< tcs
->filter_count
)
917 /* Check for conditions. */
918 if (!check_clauses(tcs
, idx
, tcs
->clause
, tcs
->clause_count
))
921 /* Add to the seek list if not already in uniq buffer. */
922 if (!add_uniqbuf(tcs
, idx
->tag_seek
[tcs
->type
]))
926 tcs
->seek_list
[tcs
->seek_list_count
] = idx
->tag_seek
[tcs
->type
];
927 tcs
->seek_flags
[tcs
->seek_list_count
] = idx
->flag
;
928 tcs
->seek_list_count
++;
933 return tcs
->seek_list_count
> 0;
937 lseek(tcs
->masterfd
, tcs
->seek_pos
* sizeof(struct index_entry
) +
938 sizeof(struct master_header
), SEEK_SET
);
940 while (read(tcs
->masterfd
, &entry
, sizeof(struct index_entry
)) ==
941 sizeof(struct index_entry
))
943 /* Check if entry has been deleted. */
944 if (entry
.flag
& FLAG_DELETED
)
947 if (tcs
->seek_list_count
== SEEK_LIST_SIZE
)
950 /* Go through all filters.. */
951 for (i
= 0; i
< tcs
->filter_count
; i
++)
953 if (entry
.tag_seek
[tcs
->filter_tag
[i
]] != tcs
->filter_seek
[i
])
959 if (i
< tcs
->filter_count
)
962 /* Check for conditions. */
963 if (!check_clauses(tcs
, &entry
, tcs
->clause
, tcs
->clause_count
))
966 /* Add to the seek list if not already in uniq buffer. */
967 if (!add_uniqbuf(tcs
, entry
.tag_seek
[tcs
->type
]))
971 tcs
->seek_list
[tcs
->seek_list_count
] = entry
.tag_seek
[tcs
->type
];
972 tcs
->seek_flags
[tcs
->seek_list_count
] = entry
.flag
;
973 tcs
->seek_list_count
++;
978 return tcs
->seek_list_count
> 0;
982 static void remove_files(void)
988 stat
.ramcache
= false;
989 remove(TAGCACHE_FILE_MASTER
);
990 for (i
= 0; i
< TAG_COUNT
; i
++)
992 if (tagcache_is_numeric_tag(i
))
995 snprintf(buf
, sizeof buf
, TAGCACHE_FILE_INDEX
, i
);
1001 static int open_master_fd(struct master_header
*hdr
, bool write
)
1006 fd
= open(TAGCACHE_FILE_MASTER
, write
? O_RDWR
: O_RDONLY
);
1009 logf("master file open failed for R/W");
1014 /* Check the header. */
1015 rc
= read(fd
, hdr
, sizeof(struct master_header
));
1016 if (hdr
->tch
.magic
!= TAGCACHE_MAGIC
|| rc
!= sizeof(struct master_header
))
1018 logf("header error");
1027 bool tagcache_search(struct tagcache_search
*tcs
, int tag
)
1029 struct tagcache_header tag_hdr
;
1030 struct master_header master_hdr
;
1033 if (tcs
->initialized
)
1034 tagcache_search_finish(tcs
);
1039 memset(tcs
, 0, sizeof(struct tagcache_search
));
1040 if (stat
.commit_step
> 0 || !stat
.ready
)
1043 tcs
->position
= sizeof(struct tagcache_header
);
1046 tcs
->seek_list_count
= 0;
1047 tcs
->filter_count
= 0;
1050 for (i
= 0; i
< TAG_COUNT
; i
++)
1053 #ifndef HAVE_TC_RAMCACHE
1054 tcs
->ramsearch
= false;
1056 tcs
->ramsearch
= stat
.ramcache
;
1059 tcs
->entry_count
= hdr
->entry_count
[tcs
->type
];
1064 if (!tagcache_is_numeric_tag(tcs
->type
))
1066 tcs
->idxfd
[tcs
->type
] = open_tag_fd(&tag_hdr
, tcs
->type
, false);
1067 if (tcs
->idxfd
[tcs
->type
] < 0)
1071 /* Always open as R/W so we can pass tcs to functions that modify data also
1072 * without failing. */
1073 tcs
->masterfd
= open_master_fd(&master_hdr
, true);
1075 if (tcs
->masterfd
< 0)
1080 tcs
->initialized
= true;
1086 void tagcache_search_set_uniqbuf(struct tagcache_search
*tcs
,
1087 void *buffer
, long length
)
1089 tcs
->unique_list
= (unsigned long *)buffer
;
1090 tcs
->unique_list_capacity
= length
/ sizeof(*tcs
->unique_list
);
1091 tcs
->unique_list_count
= 0;
1094 bool tagcache_search_add_filter(struct tagcache_search
*tcs
,
1097 if (tcs
->filter_count
== TAGCACHE_MAX_FILTERS
)
1100 if (!tagcache_is_unique_tag(tag
) || tagcache_is_numeric_tag(tag
))
1103 tcs
->filter_tag
[tcs
->filter_count
] = tag
;
1104 tcs
->filter_seek
[tcs
->filter_count
] = seek
;
1105 tcs
->filter_count
++;
1110 bool tagcache_search_add_clause(struct tagcache_search
*tcs
,
1111 struct tagcache_search_clause
*clause
)
1115 if (tcs
->clause_count
>= TAGCACHE_MAX_CLAUSES
)
1117 logf("Too many clauses");
1121 /* Check if there is already a similar filter in present (filters are
1122 * much faster than clauses).
1124 for (i
= 0; i
< tcs
->filter_count
; i
++)
1126 if (tcs
->filter_tag
[i
] == clause
->tag
)
1130 if (!tagcache_is_numeric_tag(clause
->tag
) && tcs
->idxfd
[clause
->tag
] < 0)
1134 snprintf(buf
, sizeof buf
, TAGCACHE_FILE_INDEX
, clause
->tag
);
1135 tcs
->idxfd
[clause
->tag
] = open(buf
, O_RDONLY
);
1138 tcs
->clause
[tcs
->clause_count
] = clause
;
1139 tcs
->clause_count
++;
1144 #define TAG_FILENAME_RAM(tcs) ((tcs->type == tag_filename) \
1145 ? (flag & FLAG_DIRCACHE) : 1)
1147 static bool get_next(struct tagcache_search
*tcs
)
1149 static char buf
[MAX_PATH
];
1150 struct tagfile_entry entry
;
1153 if (!tcs
->valid
|| !stat
.ready
)
1156 if (tcs
->idxfd
[tcs
->type
] < 0 && !tagcache_is_numeric_tag(tcs
->type
)
1157 #ifdef HAVE_TC_RAMCACHE
1163 /* Relative fetch. */
1164 if (tcs
->filter_count
> 0 || tcs
->clause_count
> 0
1165 || tagcache_is_numeric_tag(tcs
->type
))
1167 /* Check for end of list. */
1168 if (tcs
->seek_list_count
== 0)
1170 /* Try to fetch more. */
1171 if (!build_lookup_list(tcs
))
1178 tcs
->seek_list_count
--;
1179 flag
= tcs
->seek_flags
[tcs
->seek_list_count
];
1181 /* Seek stream to the correct position and continue to direct fetch. */
1182 if ((!tcs
->ramsearch
|| !TAG_FILENAME_RAM(tcs
))
1183 && !tagcache_is_numeric_tag(tcs
->type
))
1185 if (!open_files(tcs
, tcs
->type
))
1188 lseek(tcs
->idxfd
[tcs
->type
], tcs
->seek_list
[tcs
->seek_list_count
], SEEK_SET
);
1191 tcs
->position
= tcs
->seek_list
[tcs
->seek_list_count
];
1194 if (tagcache_is_numeric_tag(tcs
->type
))
1196 snprintf(buf
, sizeof(buf
), "%d", tcs
->position
);
1197 tcs
->result_seek
= tcs
->position
;
1199 tcs
->result_len
= strlen(buf
) + 1;
1204 #ifdef HAVE_TC_RAMCACHE
1205 if (tcs
->ramsearch
&& TAG_FILENAME_RAM(tcs
))
1207 struct tagfile_entry
*ep
;
1209 if (tcs
->entry_count
== 0)
1216 tcs
->result_seek
= tcs
->position
;
1218 # ifdef HAVE_DIRCACHE
1219 if (tcs
->type
== tag_filename
)
1221 dircache_copy_path((struct dircache_entry
*)tcs
->position
,
1224 tcs
->result_len
= strlen(buf
) + 1;
1225 tcs
->idx_id
= FLAG_GET_ATTR(flag
);
1226 tcs
->ramresult
= false;
1232 ep
= (struct tagfile_entry
*)&hdr
->tags
[tcs
->type
][tcs
->position
];
1233 tcs
->position
+= sizeof(struct tagfile_entry
) + ep
->tag_length
;
1234 tcs
->result
= ep
->tag_data
;
1235 tcs
->result_len
= strlen(tcs
->result
) + 1;
1236 tcs
->idx_id
= ep
->idx_id
;
1237 tcs
->ramresult
= true;
1244 if (!open_files(tcs
, tcs
->type
))
1247 tcs
->result_seek
= lseek(tcs
->idxfd
[tcs
->type
], 0, SEEK_CUR
);
1248 if (read(tcs
->idxfd
[tcs
->type
], &entry
, sizeof(struct tagfile_entry
)) !=
1249 sizeof(struct tagfile_entry
))
1257 if (entry
.tag_length
> (long)sizeof(buf
))
1260 logf("too long tag #2");
1264 if (read(tcs
->idxfd
[tcs
->type
], buf
, entry
.tag_length
) != entry
.tag_length
)
1267 logf("read error #4");
1272 tcs
->result_len
= strlen(tcs
->result
) + 1;
1273 tcs
->idx_id
= entry
.idx_id
;
1274 tcs
->ramresult
= false;
1279 bool tagcache_get_next(struct tagcache_search
*tcs
)
1281 while (get_next(tcs
))
1283 if (tcs
->result_len
> 1)
1290 bool tagcache_retrieve(struct tagcache_search
*tcs
, int idxid
,
1291 int tag
, char *buf
, long size
)
1293 struct index_entry idx
;
1296 if (!get_index(tcs
->masterfd
, idxid
, &idx
, true))
1299 return retrieve(tcs
, &idx
, tag
, buf
, size
);
1304 void tagcache_modify(struct tagcache_search
*tcs
, int type
, const char *text
)
1306 struct tagentry
*entry
;
1308 if (tcs
->type
!= tag_title
)
1311 /* We will need reserve buffer for this. */
1314 struct tagfile_entry
*ep
;
1316 ep
= (struct tagfile_entry
*)&hdr
->tags
[tcs
->type
][tcs
->result_seek
];
1317 tcs
->seek_list
[tcs
->seek_list_count
];
1320 entry
= find_entry_ram();
1325 void tagcache_search_finish(struct tagcache_search
*tcs
)
1329 if (!tcs
->initialized
)
1332 if (tcs
->masterfd
>= 0)
1334 close(tcs
->masterfd
);
1338 for (i
= 0; i
< TAG_COUNT
; i
++)
1340 if (tcs
->idxfd
[i
] >= 0)
1342 close(tcs
->idxfd
[i
]);
1347 tcs
->ramsearch
= false;
1349 tcs
->initialized
= 0;
1354 #if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE)
1355 static struct tagfile_entry
*get_tag(const struct index_entry
*entry
, int tag
)
1357 return (struct tagfile_entry
*)&hdr
->tags
[tag
][entry
->tag_seek
[tag
]];
1360 static long get_tag_numeric(const struct index_entry
*entry
, int tag
)
1362 return entry
->tag_seek
[tag
];
1365 bool tagcache_fill_tags(struct mp3entry
*id3
, const char *filename
)
1367 struct index_entry
*entry
;
1373 /* Find the corresponding entry in tagcache. */
1374 idx_id
= find_entry_ram(filename
, NULL
);
1375 if (idx_id
< 0 || !stat
.ramcache
)
1378 entry
= &hdr
->indices
[idx_id
];
1380 id3
->title
= get_tag(entry
, tag_title
)->tag_data
;
1381 id3
->artist
= get_tag(entry
, tag_artist
)->tag_data
;
1382 id3
->album
= get_tag(entry
, tag_album
)->tag_data
;
1383 id3
->genre_string
= get_tag(entry
, tag_genre
)->tag_data
;
1384 id3
->composer
= get_tag(entry
, tag_composer
)->tag_data
;
1385 id3
->year
= get_tag_numeric(entry
, tag_year
);
1386 id3
->tracknum
= get_tag_numeric(entry
, tag_tracknumber
);
1387 id3
->bitrate
= get_tag_numeric(entry
, tag_bitrate
);
1388 if (id3
->bitrate
== 0)
1395 static inline void write_item(const char *item
)
1397 int len
= strlen(item
) + 1;
1400 write(cachefd
, item
, len
);
1403 static int check_if_empty(char **tag
)
1407 if (*tag
== NULL
|| *tag
[0] == '\0')
1409 *tag
= "<Untagged>";
1410 return 11; /* Tag length */
1413 length
= strlen(*tag
);
1414 if (length
>= MAX_PATH
-32)
1416 logf("over length tag: %s", *tag
);
1417 *tag
[MAX_PATH
-32] = '\0';
1418 length
= MAX_PATH
-32;
1424 #define ADD_TAG(entry,tag,data) \
1426 entry.tag_offset[tag] = offset; \
1427 entry.tag_length[tag] = check_if_empty(data); \
1428 offset += entry.tag_length[tag]
1430 #if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE)
1431 static void add_tagcache(char *path
, const struct dircache_entry
*dc
)
1433 static void add_tagcache(char *path
)
1436 struct track_info track
;
1437 struct temp_file_entry entry
;
1440 char tracknumfix
[3];
1447 /* Check if the file is supported. */
1448 if (probe_file_format(path
) == AFMT_UNKNOWN
)
1451 /* Check if the file is already cached. */
1452 #if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE)
1453 if (stat
.ramcache
&& dircache_is_enabled())
1455 if (find_entry_ram(path
, dc
) >= 0)
1461 if (filenametag_fd
>= 0)
1463 if (find_entry_disk(path
) >= 0)
1468 fd
= open(path
, O_RDONLY
);
1471 logf("open fail: %s", path
);
1475 memset(&track
, 0, sizeof(struct track_info
));
1476 memset(&entry
, 0, sizeof(struct temp_file_entry
));
1477 memset(&tracknumfix
, 0, sizeof(tracknumfix
));
1478 ret
= get_metadata(&track
, fd
, path
, false);
1484 logf("-> %s", path
);
1486 /* Generate track number if missing. */
1487 if (track
.id3
.tracknum
<= 0)
1489 const char *p
= strrchr(path
, '.');
1492 p
= &path
[strlen(path
)-1];
1496 if (isdigit(*p
) && isdigit(*(p
-1)))
1498 tracknumfix
[1] = *p
--;
1499 tracknumfix
[0] = *p
;
1505 if (tracknumfix
[0] != '\0')
1507 track
.id3
.tracknum
= atoi(tracknumfix
);
1508 /* Set a flag to indicate track number has been generated. */
1509 entry
.flag
|= FLAG_TRKNUMGEN
;
1513 /* Unable to generate track number. */
1514 track
.id3
.tracknum
= -1;
1518 genrestr
= id3_get_genre(&track
.id3
);
1521 entry
.tag_offset
[tag_year
] = track
.id3
.year
;
1522 entry
.tag_offset
[tag_tracknumber
] = track
.id3
.tracknum
;
1523 entry
.tag_offset
[tag_length
] = track
.id3
.length
;
1524 entry
.tag_offset
[tag_bitrate
] = track
.id3
.bitrate
;
1527 ADD_TAG(entry
, tag_filename
, &path
);
1528 ADD_TAG(entry
, tag_title
, &track
.id3
.title
);
1529 ADD_TAG(entry
, tag_artist
, &track
.id3
.artist
);
1530 ADD_TAG(entry
, tag_album
, &track
.id3
.album
);
1531 ADD_TAG(entry
, tag_genre
, &genrestr
);
1532 ADD_TAG(entry
, tag_composer
, &track
.id3
.composer
);
1533 entry
.data_length
= offset
;
1535 /* Write the header */
1536 write(cachefd
, &entry
, sizeof(struct temp_file_entry
));
1538 /* And tags also... Correct order is critical */
1540 write_item(track
.id3
.title
);
1541 write_item(track
.id3
.artist
);
1542 write_item(track
.id3
.album
);
1543 write_item(genrestr
);
1544 write_item(track
.id3
.composer
);
1545 total_entry_count
++;
1548 static bool tempbuf_insert(char *str
, int id
, int idx_id
, bool unique
)
1550 struct tempbuf_searchidx
*index
= (struct tempbuf_searchidx
*)tempbuf
;
1551 int len
= strlen(str
)+1;
1554 unsigned *crcbuf
= (unsigned *)&tempbuf
[tempbuf_size
-4];
1557 for (i
= 0; str
[i
] != '\0' && i
< (int)sizeof(buf
)-1; i
++)
1558 buf
[i
] = tolower(str
[i
]);
1561 crc32
= crc_32(buf
, i
, 0xffffffff);
1565 /* Check if the crc does not exist -> entry does not exist for sure. */
1566 for (i
= 0; i
< tempbufidx
; i
++)
1568 if (crcbuf
[-i
] != crc32
)
1571 if (!strcasecmp(str
, index
[i
].str
))
1573 if (id
< 0 || id
>= lookup_buffer_depth
)
1575 logf("lookup buf overf.: %d", id
);
1579 lookup
[id
] = &index
[i
];
1585 /* Insert to CRC buffer. */
1586 crcbuf
[-tempbufidx
] = crc32
;
1589 /* Insert it to the buffer. */
1590 tempbuf_left
-= len
;
1591 if (tempbuf_left
- 4 < 0 || tempbufidx
>= commit_entry_count
-1)
1594 if (id
>= lookup_buffer_depth
)
1596 logf("lookup buf overf. #2: %d", id
);
1602 lookup
[id
] = &index
[tempbufidx
];
1603 index
[tempbufidx
].idlist
.id
= id
;
1606 index
[tempbufidx
].idlist
.id
= -1;
1608 index
[tempbufidx
].idlist
.next
= NULL
;
1609 index
[tempbufidx
].idx_id
= idx_id
;
1610 index
[tempbufidx
].seek
= -1;
1611 index
[tempbufidx
].str
= &tempbuf
[tempbuf_pos
];
1612 memcpy(index
[tempbufidx
].str
, str
, len
);
1619 static int compare(const void *p1
, const void *p2
)
1621 struct tempbuf_searchidx
*e1
= (struct tempbuf_searchidx
*)p1
;
1622 struct tempbuf_searchidx
*e2
= (struct tempbuf_searchidx
*)p2
;
1625 if (!strncasecmp("the ", e1, 4))
1627 if (!strncasecmp("the ", e2, 4))
1631 return strncasecmp(e1
->str
, e2
->str
, MAX_PATH
);
1634 static int tempbuf_sort(int fd
)
1636 struct tempbuf_searchidx
*index
= (struct tempbuf_searchidx
*)tempbuf
;
1637 struct tagfile_entry fe
;
1641 /* Generate reverse lookup entries. */
1642 for (i
= 0; i
< lookup_buffer_depth
; i
++)
1644 struct tempbuf_id_list
*idlist
;
1649 if (lookup
[i
]->idlist
.id
== i
)
1652 idlist
= &lookup
[i
]->idlist
;
1653 while (idlist
->next
!= NULL
)
1654 idlist
= idlist
->next
;
1656 tempbuf_left
-= sizeof(struct tempbuf_id_list
);
1657 if (tempbuf_left
- 4 < 0)
1660 idlist
->next
= (struct tempbuf_id_list
*)&tempbuf
[tempbuf_pos
];
1661 if (tempbuf_pos
& 0x03)
1663 tempbuf_pos
= (tempbuf_pos
& ~0x03) + 0x04;
1665 idlist
->next
= (struct tempbuf_id_list
*)&tempbuf
[tempbuf_pos
];
1667 tempbuf_pos
+= sizeof(struct tempbuf_id_list
);
1669 idlist
= idlist
->next
;
1671 idlist
->next
= NULL
;
1674 qsort(index
, tempbufidx
, sizeof(struct tempbuf_searchidx
), compare
);
1675 memset(lookup
, 0, lookup_buffer_depth
* sizeof(struct tempbuf_searchidx
**));
1677 for (i
= 0; i
< tempbufidx
; i
++)
1679 struct tempbuf_id_list
*idlist
= &index
[i
].idlist
;
1681 /* Fix the lookup list. */
1682 while (idlist
!= NULL
)
1684 if (idlist
->id
>= 0)
1685 lookup
[idlist
->id
] = &index
[i
];
1686 idlist
= idlist
->next
;
1689 index
[i
].seek
= lseek(fd
, 0, SEEK_CUR
);
1690 length
= strlen(index
[i
].str
) + 1;
1691 fe
.tag_length
= length
;
1692 fe
.idx_id
= index
[i
].idx_id
;
1694 /* Check the chunk alignment. */
1695 if ((fe
.tag_length
+ sizeof(struct tagfile_entry
))
1696 % TAGFILE_ENTRY_CHUNK_LENGTH
)
1698 fe
.tag_length
+= TAGFILE_ENTRY_CHUNK_LENGTH
-
1699 ((fe
.tag_length
+ sizeof(struct tagfile_entry
))
1700 % TAGFILE_ENTRY_CHUNK_LENGTH
);
1703 #ifdef TAGCACHE_STRICT_ALIGN
1704 /* Make sure the entry is long aligned. */
1705 if (index
[i
].seek
& 0x03)
1707 logf("tempbuf_sort: alignment error!");
1712 if (write(fd
, &fe
, sizeof(struct tagfile_entry
)) !=
1713 sizeof(struct tagfile_entry
))
1715 logf("tempbuf_sort: write error #1");
1719 if (write(fd
, index
[i
].str
, length
) != length
)
1721 logf("tempbuf_sort: write error #2");
1725 /* Write some padding. */
1726 if (fe
.tag_length
- length
> 0)
1727 write(fd
, "XXXXXXXX", fe
.tag_length
- length
);
1733 inline static struct tempbuf_searchidx
* tempbuf_locate(int id
)
1735 if (id
< 0 || id
>= lookup_buffer_depth
)
1742 inline static int tempbuf_find_location(int id
)
1744 struct tempbuf_searchidx
*entry
;
1746 entry
= tempbuf_locate(id
);
1753 static bool build_numeric_indices(struct tagcache_header
*h
, int tmpfd
)
1755 struct master_header tcmh
;
1756 struct index_entry idx
;
1759 struct temp_file_entry
*entrybuf
= (struct temp_file_entry
*)tempbuf
;
1761 int entries_processed
= 0;
1764 max_entries
= tempbuf_size
/ sizeof(struct temp_file_entry
) - 1;
1766 logf("Building numeric indices...");
1767 lseek(tmpfd
, sizeof(struct tagcache_header
), SEEK_SET
);
1769 if ( (masterfd
= open_master_fd(&tcmh
, true)) < 0)
1772 masterfd_pos
= lseek(masterfd
, tcmh
.tch
.entry_count
* sizeof(struct index_entry
),
1774 if (masterfd_pos
== filesize(masterfd
))
1776 logf("we can't append!");
1781 while (entries_processed
< h
->entry_count
)
1783 int count
= MIN(h
->entry_count
, max_entries
);
1785 /* Read in as many entries as possible. */
1786 for (i
= 0; i
< count
; i
++)
1788 /* Read in numeric data. */
1789 if (read(tmpfd
, &entrybuf
[i
], sizeof(struct temp_file_entry
)) !=
1790 sizeof(struct temp_file_entry
))
1792 logf("read fail #1");
1797 /* Skip string data. */
1798 lseek(tmpfd
, entrybuf
[i
].data_length
, SEEK_CUR
);
1801 /* Commit the data to the index. */
1802 for (i
= 0; i
< count
; i
++)
1804 int loc
= lseek(masterfd
, 0, SEEK_CUR
);
1806 if (read(masterfd
, &idx
, sizeof(struct index_entry
)) !=
1807 sizeof(struct index_entry
))
1809 logf("read fail #2");
1814 for (j
= 0; j
< TAG_COUNT
; j
++)
1816 if (!tagcache_is_numeric_tag(j
))
1819 idx
.tag_seek
[j
] = entrybuf
[i
].tag_offset
[j
];
1821 idx
.flag
= entrybuf
[i
].flag
;
1823 /* Write back the updated index. */
1824 lseek(masterfd
, loc
, SEEK_SET
);
1825 if (write(masterfd
, &idx
, sizeof(struct index_entry
)) !=
1826 sizeof(struct index_entry
))
1834 entries_processed
+= count
;
1835 logf("%d/%d entries processed", entries_processed
, h
->entry_count
);
1846 * == 0 temporary failure
1849 static int build_index(int index_type
, struct tagcache_header
*h
, int tmpfd
)
1852 struct tagcache_header tch
;
1853 struct master_header tcmh
;
1854 struct index_entry idxbuf
[IDX_BUF_DEPTH
];
1857 int fd
= -1, masterfd
;
1862 logf("Building index: %d", index_type
);
1864 /* Check the number of entries we need to allocate ram for. */
1865 commit_entry_count
= h
->entry_count
+ 1;
1867 masterfd
= open_master_fd(&tcmh
, false);
1870 commit_entry_count
+= tcmh
.tch
.entry_count
;
1874 /* Open the index file, which contains the tag names. */
1875 fd
= open_tag_fd(&tch
, index_type
, true);
1878 logf("tch.datasize=%d", tch
.datasize
);
1879 lookup_buffer_depth
= 1 +
1880 /* First part */ commit_entry_count
+
1881 /* Second part */ (tch
.datasize
/ TAGFILE_ENTRY_CHUNK_LENGTH
);
1885 lookup_buffer_depth
= 1 +
1886 /* First part */ commit_entry_count
+
1887 /* Second part */ 0;
1890 logf("lookup_buffer_depth=%d", lookup_buffer_depth
);
1891 logf("commit_entry_count=%d", commit_entry_count
);
1893 /* Allocate buffer for all index entries from both old and new
1896 tempbuf_pos
= commit_entry_count
* sizeof(struct tempbuf_searchidx
);
1898 /* Allocate lookup buffer. The first portion of commit_entry_count
1899 * contains the new tags in the temporary file and the second
1900 * part for locating entries already in the db.
1903 * +---------+---------------------------+
1904 * | index | position/ENTRY_CHUNK_SIZE | lookup buffer
1905 * +---------+---------------------------+
1907 * Old tags are inserted to a temporary buffer with position:
1908 * tempbuf_insert(position/ENTRY_CHUNK_SIZE, ...);
1909 * And new tags with index:
1910 * tempbuf_insert(idx, ...);
1912 * The buffer is sorted and written into tag file:
1913 * tempbuf_sort(...);
1914 * leaving master index locations messed up.
1916 * That is fixed using the lookup buffer for old tags:
1917 * new_seek = tempbuf_find_location(old_seek, ...);
1919 * new_seek = tempbuf_find_location(idx);
1921 lookup
= (struct tempbuf_searchidx
**)&tempbuf
[tempbuf_pos
];
1922 tempbuf_pos
+= lookup_buffer_depth
* sizeof(void **);
1923 memset(lookup
, 0, lookup_buffer_depth
* sizeof(void **));
1925 /* And calculate the remaining data space used mainly for storing
1926 * tag data (strings). */
1927 tempbuf_left
= tempbuf_size
- tempbuf_pos
- 8;
1928 if (tempbuf_left
- TAGFILE_ENTRY_AVG_LENGTH
* commit_entry_count
< 0)
1930 logf("Buffer way too small!");
1937 * If tag file contains unique tags (sorted index), we will load
1938 * it entirely into memory so we can resort it later for use with
1941 if (tagcache_is_sorted_tag(index_type
))
1943 logf("loading tags...");
1944 for (i
= 0; i
< tch
.entry_count
; i
++)
1946 struct tagfile_entry entry
;
1947 int loc
= lseek(fd
, 0, SEEK_CUR
);
1950 if (read(fd
, &entry
, sizeof(struct tagfile_entry
))
1951 != sizeof(struct tagfile_entry
))
1953 logf("read error #7");
1958 if (entry
.tag_length
>= (int)sizeof(buf
))
1960 logf("too long tag #3");
1965 if (read(fd
, buf
, entry
.tag_length
) != entry
.tag_length
)
1967 logf("read error #8");
1972 /* Skip deleted entries. */
1977 * Save the tag and tag id in the memory buffer. Tag id
1978 * is saved so we can later reindex the master lookup
1979 * table when the index gets resorted.
1981 ret
= tempbuf_insert(buf
, loc
/TAGFILE_ENTRY_CHUNK_LENGTH
1982 + commit_entry_count
, entry
.idx_id
,
1983 tagcache_is_unique_tag(index_type
));
1994 tempbufidx
= tch
.entry_count
;
1999 * Creating new index file to store the tags. No need to preload
2000 * anything whether the index type is sorted or not.
2002 snprintf(buf
, sizeof buf
, TAGCACHE_FILE_INDEX
, index_type
);
2003 fd
= open(buf
, O_WRONLY
| O_CREAT
| O_TRUNC
);
2006 logf("%s open fail", buf
);
2010 tch
.magic
= TAGCACHE_MAGIC
;
2011 tch
.entry_count
= 0;
2014 if (write(fd
, &tch
, sizeof(struct tagcache_header
)) !=
2015 sizeof(struct tagcache_header
))
2017 logf("header write failed");
2023 /* Loading the tag lookup file as "master file". */
2024 logf("Loading index file");
2025 masterfd
= open(TAGCACHE_FILE_MASTER
, O_RDWR
);
2029 logf("Creating new index");
2030 masterfd
= open(TAGCACHE_FILE_MASTER
, O_WRONLY
| O_CREAT
| O_TRUNC
);
2034 logf("Failure to create index file");
2039 /* Write the header (write real values later). */
2040 memset(&tcmh
, 0, sizeof(struct master_header
));
2042 tcmh
.tch
.entry_count
= 0;
2043 tcmh
.tch
.datasize
= 0;
2044 write(masterfd
, &tcmh
, sizeof(struct master_header
));
2046 masterfd_pos
= lseek(masterfd
, 0, SEEK_CUR
);
2052 * Master file already exists so we need to process the current
2057 if (read(masterfd
, &tcmh
, sizeof(struct master_header
)) !=
2058 sizeof(struct master_header
) || tcmh
.tch
.magic
!= TAGCACHE_MAGIC
)
2060 logf("header error");
2067 * If we reach end of the master file, we need to expand it to
2068 * hold new tags. If the current index is not sorted, we can
2069 * simply append new data to end of the file.
2070 * However, if the index is sorted, we need to update all tag
2071 * pointers in the master file for the current index.
2073 masterfd_pos
= lseek(masterfd
, tcmh
.tch
.entry_count
* sizeof(struct index_entry
),
2075 if (masterfd_pos
== filesize(masterfd
))
2077 logf("appending...");
2083 * Load new unique tags in memory to be sorted later and added
2084 * to the master lookup file.
2086 if (tagcache_is_sorted_tag(index_type
))
2088 lseek(tmpfd
, sizeof(struct tagcache_header
), SEEK_SET
);
2089 /* h is the header of the temporary file containing new tags. */
2090 logf("inserting new tags...");
2091 for (i
= 0; i
< h
->entry_count
; i
++)
2093 struct temp_file_entry entry
;
2095 if (read(tmpfd
, &entry
, sizeof(struct temp_file_entry
)) !=
2096 sizeof(struct temp_file_entry
))
2098 logf("read fail #1");
2104 if (entry
.tag_length
[index_type
] >= (long)sizeof(buf
))
2106 logf("too long entry!");
2111 lseek(tmpfd
, entry
.tag_offset
[index_type
], SEEK_CUR
);
2112 if (read(tmpfd
, buf
, entry
.tag_length
[index_type
]) !=
2113 entry
.tag_length
[index_type
])
2115 logf("read fail #3");
2120 if (tagcache_is_unique_tag(index_type
))
2121 error
= !tempbuf_insert(buf
, i
, -1, true);
2123 error
= !tempbuf_insert(buf
, i
, tcmh
.tch
.entry_count
+ i
, false);
2127 logf("insert error");
2132 lseek(tmpfd
, entry
.data_length
- entry
.tag_offset
[index_type
] -
2133 entry
.tag_length
[index_type
], SEEK_CUR
);
2138 /* Sort the buffer data and write it to the index file. */
2139 lseek(fd
, sizeof(struct tagcache_header
), SEEK_SET
);
2140 i
= tempbuf_sort(fd
);
2143 logf("sorted %d tags", i
);
2146 * Now update all indexes in the master lookup file.
2148 logf("updating indices...");
2149 lseek(masterfd
, sizeof(struct master_header
), SEEK_SET
);
2150 for (i
= 0; i
< tcmh
.tch
.entry_count
; i
+= idxbuf_pos
)
2153 int loc
= lseek(masterfd
, 0, SEEK_CUR
);
2155 idxbuf_pos
= MIN(tcmh
.tch
.entry_count
- i
, IDX_BUF_DEPTH
);
2157 if (read(masterfd
, idxbuf
, sizeof(struct index_entry
)*idxbuf_pos
) !=
2158 (int)sizeof(struct index_entry
)*idxbuf_pos
)
2160 logf("read fail #2");
2164 lseek(masterfd
, loc
, SEEK_SET
);
2166 for (j
= 0; j
< idxbuf_pos
; j
++)
2168 if (idxbuf
[j
].flag
& FLAG_DELETED
)
2170 /* We can just ignore deleted entries. */
2171 idxbuf
[j
].tag_seek
[index_type
] = 0;
2175 idxbuf
[j
].tag_seek
[index_type
] = tempbuf_find_location(
2176 idxbuf
[j
].tag_seek
[index_type
]/TAGFILE_ENTRY_CHUNK_LENGTH
2177 + commit_entry_count
);
2179 if (idxbuf
[j
].tag_seek
[index_type
] < 0)
2181 logf("update error: %d/%d", i
+j
, tcmh
.tch
.entry_count
);
2189 /* Write back the updated index. */
2190 if (write(masterfd
, idxbuf
, sizeof(struct index_entry
)*idxbuf_pos
) !=
2191 (int)sizeof(struct index_entry
)*idxbuf_pos
)
2202 * Walk through the temporary file containing the new tags.
2204 // build_normal_index(h, tmpfd, masterfd, idx);
2205 logf("updating new indices...");
2206 lseek(masterfd
, masterfd_pos
, SEEK_SET
);
2207 lseek(tmpfd
, sizeof(struct tagcache_header
), SEEK_SET
);
2208 lseek(fd
, 0, SEEK_END
);
2209 for (i
= 0; i
< h
->entry_count
; i
+= idxbuf_pos
)
2213 idxbuf_pos
= MIN(h
->entry_count
- i
, IDX_BUF_DEPTH
);
2216 memset(idxbuf
, 0, sizeof(struct index_entry
)*IDX_BUF_DEPTH
);
2220 int loc
= lseek(masterfd
, 0, SEEK_CUR
);
2222 if (read(masterfd
, idxbuf
, sizeof(struct index_entry
)*idxbuf_pos
) !=
2223 (int)sizeof(struct index_entry
)*idxbuf_pos
)
2225 logf("read fail #2");
2229 lseek(masterfd
, loc
, SEEK_SET
);
2232 /* Read entry headers. */
2233 for (j
= 0; j
< idxbuf_pos
; j
++)
2235 if (!tagcache_is_sorted_tag(index_type
))
2237 struct temp_file_entry entry
;
2238 struct tagfile_entry fe
;
2240 if (read(tmpfd
, &entry
, sizeof(struct temp_file_entry
)) !=
2241 sizeof(struct temp_file_entry
))
2243 logf("read fail #1");
2249 if (entry
.tag_length
[index_type
] >= (int)sizeof(buf
))
2251 logf("too long entry!");
2252 logf("length=%d", entry
.tag_length
[index_type
]);
2253 logf("pos=0x%02x", lseek(tmpfd
, 0, SEEK_CUR
));
2258 lseek(tmpfd
, entry
.tag_offset
[index_type
], SEEK_CUR
);
2259 if (read(tmpfd
, buf
, entry
.tag_length
[index_type
]) !=
2260 entry
.tag_length
[index_type
])
2262 logf("read fail #3");
2263 logf("offset=0x%02x", entry
.tag_offset
[index_type
]);
2264 logf("length=0x%02x", entry
.tag_length
[index_type
]);
2269 /* Write to index file. */
2270 idxbuf
[j
].tag_seek
[index_type
] = lseek(fd
, 0, SEEK_CUR
);
2271 fe
.tag_length
= entry
.tag_length
[index_type
];
2272 fe
.idx_id
= tcmh
.tch
.entry_count
+ i
+ j
;
2273 write(fd
, &fe
, sizeof(struct tagfile_entry
));
2274 write(fd
, buf
, fe
.tag_length
);
2278 lseek(tmpfd
, entry
.data_length
- entry
.tag_offset
[index_type
] -
2279 entry
.tag_length
[index_type
], SEEK_CUR
);
2283 /* Locate the correct entry from the sorted array. */
2284 idxbuf
[j
].tag_seek
[index_type
] = tempbuf_find_location(i
+ j
);
2285 if (idxbuf
[j
].tag_seek
[index_type
] < 0)
2287 logf("entry not found (%d)");
2295 if (write(masterfd
, idxbuf
, sizeof(struct index_entry
)*idxbuf_pos
) !=
2296 (int)sizeof(struct index_entry
)*idxbuf_pos
)
2298 logf("tagcache: write fail #4");
2307 /* Finally write the header. */
2308 tch
.magic
= TAGCACHE_MAGIC
;
2309 tch
.entry_count
= tempbufidx
;
2310 tch
.datasize
= lseek(fd
, 0, SEEK_END
) - sizeof(struct tagcache_header
);
2311 lseek(fd
, 0, SEEK_SET
);
2312 write(fd
, &tch
, sizeof(struct tagcache_header
));
2314 if (index_type
!= tag_filename
)
2315 h
->datasize
+= tch
.datasize
;
2316 logf("s:%d/%d/%d", index_type
, tch
.datasize
, h
->datasize
);
2328 static bool commit(void)
2330 struct tagcache_header tch
;
2331 struct master_header tcmh
;
2335 #ifdef HAVE_DIRCACHE
2336 bool dircache_buffer_stolen
= false;
2338 bool local_allocation
= false;
2340 logf("committing tagcache");
2345 tmpfd
= open(TAGCACHE_FILE_TEMP
, O_RDONLY
);
2348 logf("nothing to commit");
2352 /* Load the header. */
2353 len
= sizeof(struct tagcache_header
);
2354 rc
= read(tmpfd
, &tch
, len
);
2356 if (tch
.magic
!= TAGCACHE_MAGIC
|| rc
!= len
)
2358 logf("incorrect header");
2360 remove(TAGCACHE_FILE_TEMP
);
2364 if (tch
.entry_count
== 0)
2366 logf("nothing to commit");
2368 remove(TAGCACHE_FILE_TEMP
);
2372 #ifdef HAVE_EEPROM_SETTINGS
2373 remove(TAGCACHE_STATEFILE
);
2376 /* At first be sure to unload the ramcache! */
2377 #ifdef HAVE_TC_RAMCACHE
2378 stat
.ramcache
= false;
2383 /* Try to steal every buffer we can :) */
2384 if (tempbuf_size
== 0)
2385 local_allocation
= true;
2387 #ifdef HAVE_DIRCACHE
2388 if (tempbuf_size
== 0)
2390 /* Try to steal the dircache buffer. */
2391 tempbuf
= dircache_steal_buffer(&tempbuf_size
);
2392 tempbuf_size
&= ~0x03;
2394 if (tempbuf_size
> 0)
2396 dircache_buffer_stolen
= true;
2401 #ifdef HAVE_TC_RAMCACHE
2402 if (tempbuf_size
== 0 && stat
.ramcache_allocated
> 0)
2404 tempbuf
= (char *)(hdr
+ 1);
2405 tempbuf_size
= stat
.ramcache_allocated
- sizeof(struct ramcache_header
) - 128;
2406 tempbuf_size
&= ~0x03;
2410 /* And finally fail if there are no buffers available. */
2411 if (tempbuf_size
== 0)
2413 logf("delaying commit until next boot");
2414 stat
.commit_delayed
= true;
2420 logf("commit %d entries...", tch
.entry_count
);
2422 /* Now create the index files. */
2423 stat
.commit_step
= 0;
2425 stat
.commit_delayed
= false;
2427 for (i
= 0; i
< TAG_COUNT
; i
++)
2431 if (tagcache_is_numeric_tag(i
))
2435 ret
= build_index(i
, &tch
, tmpfd
);
2439 logf("tagcache failed init");
2443 stat
.commit_delayed
= true;
2444 stat
.commit_step
= 0;
2450 build_numeric_indices(&tch
, tmpfd
);
2452 stat
.commit_step
= 0;
2454 /* Update the master index headers. */
2455 if ( (masterfd
= open_master_fd(&tcmh
, true)) < 0)
2461 tcmh
.tch
.entry_count
+= tch
.entry_count
;
2462 tcmh
.tch
.datasize
= sizeof(struct master_header
)
2463 + sizeof(struct index_entry
) * tcmh
.tch
.entry_count
2466 lseek(masterfd
, 0, SEEK_SET
);
2467 write(masterfd
, &tcmh
, sizeof(struct master_header
));
2470 logf("tagcache committed");
2471 remove(TAGCACHE_FILE_TEMP
);
2474 if (local_allocation
)
2480 #ifdef HAVE_DIRCACHE
2481 /* Rebuild the dircache, if we stole the buffer. */
2482 if (dircache_buffer_stolen
)
2486 #ifdef HAVE_TC_RAMCACHE
2487 /* Reload tagcache. */
2488 if (stat
.ramcache_allocated
> 0)
2489 tagcache_start_scan();
2497 static void allocate_tempbuf(void)
2499 /* Yeah, malloc would be really nice now :) */
2500 tempbuf
= (char *)(((long)audiobuf
& ~0x03) + 0x04);
2501 tempbuf_size
= (long)audiobufend
- (long)audiobuf
- 4;
2502 audiobuf
+= tempbuf_size
;
2505 static void free_tempbuf(void)
2507 if (tempbuf_size
== 0)
2510 audiobuf
-= tempbuf_size
;
2515 static bool update_current_serial(long serial
)
2517 struct master_header myhdr
;
2520 if ( (fd
= open_master_fd(&myhdr
, true)) < 0)
2523 myhdr
.serial
= serial
;
2524 current_serial
= serial
;
2526 #ifdef HAVE_TC_RAMCACHE
2528 hdr
->h
.serial
= serial
;
2532 lseek(fd
, 0, SEEK_SET
);
2533 write(fd
, &myhdr
, sizeof(struct master_header
));
2539 long tagcache_increase_serial(void)
2547 if (!update_current_serial(current_serial
+ 1))
2550 return current_serial
;
2553 long tagcache_get_serial(void)
2555 return current_serial
;
2558 static bool modify_numeric_entry(int masterfd
, int idx_id
, int tag
, long data
)
2560 struct index_entry idx
;
2565 if (!tagcache_is_numeric_tag(tag
))
2568 if (!get_index(masterfd
, idx_id
, &idx
, false))
2571 idx
.tag_seek
[tag
] = data
;
2572 idx
.flag
|= FLAG_DIRTYNUM
;
2574 return write_index(masterfd
, idx_id
, &idx
);
2577 bool tagcache_modify_numeric_entry(struct tagcache_search
*tcs
,
2580 struct master_header myhdr
;
2582 if (tcs
->masterfd
< 0)
2584 if ( (tcs
->masterfd
= open_master_fd(&myhdr
, true)) < 0)
2588 return modify_numeric_entry(tcs
->masterfd
, tcs
->idx_id
, tag
, data
);
2591 static bool write_tag(int fd
, const char *tagstr
, const char *datastr
)
2596 snprintf(buf
, sizeof buf
, "%s=\"", tagstr
);
2597 for (i
= strlen(buf
); i
< (long)sizeof(buf
)-2; i
++)
2599 if (*datastr
== '\0')
2602 if (*datastr
== '"')
2609 buf
[i
] = *(datastr
++);
2612 strcpy(&buf
[i
], "\" ");
2614 write(fd
, buf
, i
+ 2);
2619 static bool read_tag(char *dest
, long size
,
2620 const char *src
, const char *tagstr
)
2623 char current_tag
[32];
2625 while (*src
!= '\0')
2627 /* Skip all whitespace */
2635 /* Read in tag name */
2636 while (*src
!= '=' && *src
!= ' ')
2638 current_tag
[pos
] = *src
;
2642 if (*src
== '\0' || pos
>= (long)sizeof(current_tag
))
2645 current_tag
[pos
] = '\0';
2647 /* Read in tag data */
2649 /* Find the start. */
2650 while (*src
!= '"' && *src
!= '\0')
2653 if (*src
== '\0' || *(++src
) == '\0')
2656 /* Read the data. */
2657 for (pos
= 0; pos
< size
; pos
++)
2662 if (*src
== '\\' && *(src
+1) == '"')
2684 if (!strcasecmp(tagstr
, current_tag
))
2691 static int parse_changelog_line(int line_n
, const char *buf
, void *parameters
)
2693 struct index_entry idx
;
2694 char tag_data
[MAX_PATH
];
2696 long masterfd
= (long)parameters
;
2697 const int import_tags
[] = { tag_playcount
, tag_playtime
, tag_lastplayed
};
2704 logf("%d/%s", line_n
, buf
);
2705 if (!read_tag(tag_data
, sizeof tag_data
, buf
, "filename"))
2707 logf("filename missing");
2712 idx_id
= find_index(tag_data
);
2715 logf("entry not found");
2719 if (!get_index(masterfd
, idx_id
, &idx
, false))
2721 logf("failed to retrieve index entry");
2725 /* Stop if tag has already been modified. */
2726 if (idx
.flag
& FLAG_DIRTYNUM
)
2729 logf("import: %s", tag_data
);
2731 idx
.flag
|= FLAG_DIRTYNUM
;
2732 for (i
= 0; i
< (long)(sizeof(import_tags
)/sizeof(import_tags
[0])); i
++)
2736 if (!read_tag(tag_data
, sizeof tag_data
, buf
,
2737 tagcache_tag_to_str(import_tags
[i
])))
2742 data
= atoi(tag_data
);
2746 idx
.tag_seek
[import_tags
[i
]] = data
;
2748 if (import_tags
[i
] == tag_lastplayed
&& data
> current_serial
)
2749 current_serial
= data
;
2752 return write_index(masterfd
, idx_id
, &idx
) ? 0 : -5;
2755 bool tagcache_import_changelog(void)
2757 struct master_header myhdr
;
2758 struct tagcache_header tch
;
2769 clfd
= open(TAGCACHE_FILE_CHANGELOG
, O_RDONLY
);
2772 logf("failure to open changelog");
2776 if ( (masterfd
= open_master_fd(&myhdr
, true)) < 0)
2784 filenametag_fd
= open_tag_fd(&tch
, tag_filename
, false);
2786 fast_readline(clfd
, buf
, sizeof buf
, (long *)masterfd
,
2787 parse_changelog_line
);
2792 if (filenametag_fd
>= 0)
2793 close(filenametag_fd
);
2797 update_current_serial(current_serial
);
2802 bool tagcache_create_changelog(struct tagcache_search
*tcs
)
2804 struct master_header myhdr
;
2805 struct index_entry idx
;
2814 if (!tagcache_search(tcs
, tag_filename
))
2817 /* Initialize the changelog */
2818 clfd
= open(TAGCACHE_FILE_CHANGELOG
, O_WRONLY
| O_CREAT
| O_TRUNC
);
2821 logf("failure to open changelog");
2825 if (tcs
->masterfd
< 0)
2827 if ( (tcs
->masterfd
= open_master_fd(&myhdr
, false)) < 0)
2832 lseek(tcs
->masterfd
, 0, SEEK_SET
);
2833 read(tcs
->masterfd
, &myhdr
, sizeof(struct master_header
));
2836 write(clfd
, "## Changelog version 1\n", 23);
2838 for (i
= 0; i
< myhdr
.tch
.entry_count
; i
++)
2840 if (read(tcs
->masterfd
, &idx
, sizeof(struct index_entry
))
2841 != sizeof(struct index_entry
))
2843 logf("read error #9");
2844 tagcache_search_finish(tcs
);
2849 /* Skip until the entry found has been modified. */
2850 if (! (idx
.flag
& FLAG_DIRTYNUM
) )
2853 /* Now retrieve all tags. */
2854 for (j
= 0; j
< TAG_COUNT
; j
++)
2856 if (tagcache_is_numeric_tag(j
))
2858 snprintf(temp
, sizeof temp
, "%d", idx
.tag_seek
[j
]);
2859 write_tag(clfd
, tagcache_tag_to_str(j
), temp
);
2864 tagcache_retrieve(tcs
, i
, tcs
->type
, buf
, sizeof buf
);
2865 write_tag(clfd
, tagcache_tag_to_str(j
), buf
);
2868 write(clfd
, "\n", 1);
2874 tagcache_search_finish(tcs
);
2879 static bool delete_entry(long idx_id
)
2883 struct index_entry idx
, myidx
;
2884 struct master_header myhdr
;
2886 int in_use
[TAG_COUNT
];
2888 logf("delete_entry(): %d", idx_id
);
2890 #ifdef HAVE_TC_RAMCACHE
2891 /* At first mark the entry removed from ram cache. */
2893 hdr
->indices
[idx_id
].flag
|= FLAG_DELETED
;
2896 if ( (fd
= open_master_fd(&myhdr
, true) ) < 0)
2899 lseek(fd
, idx_id
* sizeof(struct index_entry
), SEEK_CUR
);
2900 if (read(fd
, &myidx
, sizeof(struct index_entry
))
2901 != sizeof(struct index_entry
))
2903 logf("delete_entry(): read error");
2908 myidx
.flag
|= FLAG_DELETED
;
2909 lseek(fd
, -sizeof(struct index_entry
), SEEK_CUR
);
2910 if (write(fd
, &myidx
, sizeof(struct index_entry
))
2911 != sizeof(struct index_entry
))
2913 logf("delete_entry(): write_error");
2918 /* Now check which tags are no longer in use (if any) */
2919 for (tag
= 0; tag
< TAG_COUNT
; tag
++)
2922 lseek(fd
, sizeof(struct master_header
), SEEK_SET
);
2923 for (i
= 0; i
< myhdr
.tch
.entry_count
; i
++)
2925 if (read(fd
, &idx
, sizeof(struct index_entry
))
2926 != sizeof(struct index_entry
))
2928 logf("delete_entry(): read error #2");
2933 if (idx
.flag
& FLAG_DELETED
)
2936 for (tag
= 0; tag
< TAG_COUNT
; tag
++)
2938 if (tagcache_is_numeric_tag(tag
))
2941 if (idx
.tag_seek
[tag
] == myidx
.tag_seek
[tag
])
2948 /* Now delete all tags no longer in use. */
2949 for (tag
= 0; tag
< TAG_COUNT
; tag
++)
2951 if (tagcache_is_numeric_tag(tag
))
2956 logf("in use: %d/%d", tag
, in_use
[tag
]);
2960 /* Open the index file, which contains the tag names. */
2961 snprintf(buf
, sizeof buf
, TAGCACHE_FILE_INDEX
, tag
);
2962 fd
= open(buf
, O_RDWR
);
2966 logf("open failed");
2970 /* Skip the header block */
2971 lseek(fd
, myidx
.tag_seek
[tag
] + sizeof(struct tagfile_entry
), SEEK_SET
);
2973 /* Debug, print 10 first characters of the tag
2976 logf("TAG:%s", buf);
2977 lseek(fd, -10, SEEK_CUR);
2980 /* Write first data byte in tag as \0 */
2983 /* Now tag data has been removed */
2991 * Returns true if there is an event waiting in the queue
2992 * that requires the current operation to be aborted.
2994 static bool check_event_queue(void)
2998 queue_wait_w_tmo(&tagcache_queue
, &ev
, 0);
3003 case SYS_USB_CONNECTED
:
3004 /* Put the event back into the queue. */
3005 queue_post(&tagcache_queue
, ev
.id
, ev
.data
);
3012 #ifdef HAVE_TC_RAMCACHE
3013 static bool allocate_tagcache(void)
3015 struct master_header tcmh
;
3018 /* Load the header. */
3019 if ( (fd
= open_master_fd(&tcmh
, false)) < 0)
3028 * Now calculate the required cache size plus
3029 * some extra space for alignment fixes.
3031 stat
.ramcache_allocated
= tcmh
.tch
.datasize
+ 128 + TAGCACHE_RESERVE
+
3032 sizeof(struct ramcache_header
) + TAG_COUNT
*sizeof(void *);
3033 hdr
= buffer_alloc(stat
.ramcache_allocated
+ 128);
3034 memset(hdr
, 0, sizeof(struct ramcache_header
));
3035 memcpy(&hdr
->h
, &tcmh
, sizeof(struct master_header
));
3036 hdr
->indices
= (struct index_entry
*)(hdr
+ 1);
3037 logf("tagcache: %d bytes allocated.", stat
.ramcache_allocated
);
3042 # ifdef HAVE_EEPROM_SETTINGS
3043 static bool tagcache_dumpload(void)
3045 struct statefile_header shdr
;
3050 fd
= open(TAGCACHE_STATEFILE
, O_RDONLY
);
3053 logf("no tagcache statedump");
3057 /* Check the statefile memory placement */
3058 hdr
= buffer_alloc(0);
3059 rc
= read(fd
, &shdr
, sizeof(struct statefile_header
));
3060 if (rc
!= sizeof(struct statefile_header
)
3061 /* || (long)hdr != (long)shdr.hdr */)
3063 logf("incorrect statefile");
3069 offpos
= (long)hdr
- (long)shdr
.hdr
;
3071 /* Lets allocate real memory and load it */
3072 hdr
= buffer_alloc(shdr
.stat
.ramcache_allocated
);
3073 rc
= read(fd
, hdr
, shdr
.stat
.ramcache_allocated
);
3076 if (rc
!= shdr
.stat
.ramcache_allocated
)
3078 logf("read failure!");
3083 memcpy(&stat
, &shdr
.stat
, sizeof(struct tagcache_stat
));
3085 /* Now fix the pointers */
3086 hdr
->indices
= (struct index_entry
*)((long)hdr
->indices
+ offpos
);
3087 for (i
= 0; i
< TAG_COUNT
; i
++)
3088 hdr
->tags
[i
] += offpos
;
3093 static bool tagcache_dumpsave(void)
3095 struct statefile_header shdr
;
3101 fd
= open(TAGCACHE_STATEFILE
, O_WRONLY
| O_CREAT
| O_TRUNC
);
3104 logf("failed to create a statedump");
3108 /* Create the header */
3110 memcpy(&shdr
.stat
, &stat
, sizeof(struct tagcache_stat
));
3111 write(fd
, &shdr
, sizeof(struct statefile_header
));
3113 /* And dump the data too */
3114 write(fd
, hdr
, stat
.ramcache_allocated
);
3121 static bool load_tagcache(void)
3123 struct tagcache_header
*tch
;
3124 long bytesleft
= stat
.ramcache_allocated
;
3125 struct index_entry
*idx
;
3129 int yield_count
= 0;
3131 # ifdef HAVE_DIRCACHE
3132 while (dircache_is_initializing())
3136 logf("loading tagcache to ram...");
3138 fd
= open(TAGCACHE_FILE_MASTER
, O_RDONLY
);
3141 logf("tagcache open failed");
3145 if (read(fd
, &hdr
->h
, sizeof(struct master_header
))
3146 != sizeof(struct master_header
)
3147 || hdr
->h
.tch
.magic
!= TAGCACHE_MAGIC
)
3149 logf("incorrect header");
3155 /* Load the master index table. */
3156 for (i
= 0; i
< hdr
->h
.tch
.entry_count
; i
++)
3158 rc
= read(fd
, idx
, sizeof(struct index_entry
));
3159 if (rc
!= sizeof(struct index_entry
))
3161 logf("read error #10");
3166 bytesleft
-= sizeof(struct index_entry
);
3167 if (bytesleft
< 0 || ((long)idx
- (long)hdr
->indices
) >= stat
.ramcache_allocated
)
3169 logf("too big tagcache.");
3179 /* Load the tags. */
3181 for (tag
= 0; tag
< TAG_COUNT
; tag
++)
3183 struct tagfile_entry
*fe
;
3186 if (tagcache_is_numeric_tag(tag
))
3189 //p = ((void *)p+1);
3190 p
= (char *)((long)p
& ~0x03) + 0x04;
3193 /* Check the header. */
3194 tch
= (struct tagcache_header
*)p
;
3195 p
+= sizeof(struct tagcache_header
);
3197 if ( (fd
= open_tag_fd(tch
, tag
, false)) < 0)
3200 for (hdr
->entry_count
[tag
] = 0;
3201 hdr
->entry_count
[tag
] < tch
->entry_count
;
3202 hdr
->entry_count
[tag
]++)
3206 if (yield_count
++ == 100)
3209 /* Abort if we got a critical event in queue */
3210 if (check_event_queue())
3215 fe
= (struct tagfile_entry
*)p
;
3216 pos
= lseek(fd
, 0, SEEK_CUR
);
3217 rc
= read(fd
, fe
, sizeof(struct tagfile_entry
));
3218 if (rc
!= sizeof(struct tagfile_entry
))
3220 /* End of lookup table. */
3221 logf("read error #11");
3226 /* We have a special handling for the filename tags. */
3227 if (tag
== tag_filename
)
3229 # ifdef HAVE_DIRCACHE
3230 const struct dircache_entry
*dc
;
3233 // FIXME: This is wrong!
3234 // idx = &hdr->indices[hdr->entry_count[i]];
3235 idx
= &hdr
->indices
[fe
->idx_id
];
3237 if (fe
->tag_length
>= (long)sizeof(buf
)-1)
3241 logf("TAG:%s", buf
);
3242 logf("too long filename");
3247 rc
= read(fd
, buf
, fe
->tag_length
);
3248 if (rc
!= fe
->tag_length
)
3250 logf("read error #12");
3255 /* Check if the entry has already been removed */
3256 if (idx
->flag
& FLAG_DELETED
)
3259 /* This flag must not be used yet. */
3260 if (idx
->flag
& FLAG_DIRCACHE
)
3262 logf("internal error!");
3267 if (idx
->tag_seek
[tag
] != pos
)
3269 logf("corrupt data structures!");
3274 # ifdef HAVE_DIRCACHE
3275 if (dircache_is_enabled())
3277 dc
= dircache_get_entry_ptr(buf
);
3280 logf("Entry no longer valid.");
3282 delete_entry(fe
->idx_id
);
3286 idx
->flag
|= FLAG_DIRCACHE
;
3287 FLAG_SET_ATTR(idx
->flag
, fe
->idx_id
);
3288 idx
->tag_seek
[tag_filename
] = (long)dc
;
3294 # if 0 /* Maybe we could enable this for flash players. Too slow otherwise. */
3295 /* Check if entry has been removed. */
3296 if (global_settings
.tagcache_autoupdate
)
3300 testfd
= open(buf
, O_RDONLY
);
3303 logf("Entry no longer valid.");
3305 delete_entry(fe
->idx_id
);
3316 bytesleft
-= sizeof(struct tagfile_entry
) + fe
->tag_length
;
3319 logf("too big tagcache #2");
3320 logf("tl: %d", fe
->tag_length
);
3321 logf("bl: %d", bytesleft
);
3327 rc
= read(fd
, fe
->tag_data
, fe
->tag_length
);
3330 if (rc
!= fe
->tag_length
)
3332 logf("read error #13");
3333 logf("rc=0x%04x", rc
); // 0x431
3334 logf("len=0x%04x", fe
->tag_length
); // 0x4000
3335 logf("pos=0x%04x", lseek(fd
, 0, SEEK_CUR
)); // 0x433
3336 logf("tag=0x%02x", tag
); // 0x00
3344 stat
.ramcache_used
= stat
.ramcache_allocated
- bytesleft
;
3345 logf("tagcache loaded into ram!");
3349 #endif /* HAVE_TC_RAMCACHE */
3351 static bool check_deleted_files(void)
3355 struct tagfile_entry tfe
;
3357 logf("reverse scan...");
3358 snprintf(buf
, sizeof buf
, TAGCACHE_FILE_INDEX
, tag_filename
);
3359 fd
= open(buf
, O_RDONLY
);
3363 logf("%s open fail", buf
);
3367 lseek(fd
, sizeof(struct tagcache_header
), SEEK_SET
);
3368 while (read(fd
, &tfe
, sizeof(struct tagfile_entry
))
3369 == sizeof(struct tagfile_entry
) && !check_event_queue())
3371 if (tfe
.tag_length
>= (long)sizeof(buf
)-1)
3373 logf("too long tag");
3378 if (read(fd
, buf
, tfe
.tag_length
) != tfe
.tag_length
)
3380 logf("read error #14");
3385 /* Now check if the file exists. */
3386 testfd
= open(buf
, O_RDONLY
);
3389 logf("Entry no longer valid.");
3391 delete_entry(tfe
.idx_id
);
3403 static bool check_dir(const char *dirname
)
3407 int success
= false;
3409 dir
= opendir_cached(dirname
);
3412 logf("tagcache: opendir_cached() failed");
3416 /* Recursively scan the dir. */
3417 while (!check_event_queue())
3419 struct dircache_entry
*entry
;
3421 entry
= readdir_cached(dir
);
3429 if (!strcmp(entry
->d_name
, ".") ||
3430 !strcmp(entry
->d_name
, ".."))
3435 len
= strlen(curpath
);
3436 snprintf(&curpath
[len
], curpath_size
- len
, "/%s",
3439 processed_dir_count
++;
3440 if (entry
->attribute
& ATTR_DIRECTORY
)
3443 #if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE)
3444 add_tagcache(curpath
, dir
->internal_entry
);
3446 add_tagcache(curpath
);
3449 curpath
[len
] = '\0';
3452 closedir_cached(dir
);
3457 static void build_tagcache(void)
3459 struct tagcache_header header
;
3464 total_entry_count
= 0;
3465 processed_dir_count
= 0;
3467 #ifdef HAVE_DIRCACHE
3468 while (dircache_is_initializing())
3472 logf("updating tagcache");
3474 cachefd
= open(TAGCACHE_FILE_TEMP
, O_RDONLY
);
3477 logf("skipping, cache already waiting for commit");
3482 cachefd
= open(TAGCACHE_FILE_TEMP
, O_RDWR
| O_CREAT
| O_TRUNC
);
3485 logf("master file open failed");
3489 filenametag_fd
= open_tag_fd(&header
, tag_filename
, false);
3491 cpu_boost_id(true, CPUBOOSTID_TAGCACHE
);
3493 /* Scan for new files. */
3494 memset(&header
, 0, sizeof(struct tagcache_header
));
3495 write(cachefd
, &header
, sizeof(struct tagcache_header
));
3497 //strcpy(curpath, "/Best");
3498 ret
= check_dir("/");
3500 /* Write the header. */
3501 header
.magic
= TAGCACHE_MAGIC
;
3502 header
.datasize
= data_size
;
3503 header
.entry_count
= total_entry_count
;
3504 lseek(cachefd
, 0, SEEK_SET
);
3505 write(cachefd
, &header
, sizeof(struct tagcache_header
));
3508 if (filenametag_fd
>= 0)
3510 close(filenametag_fd
);
3511 filenametag_fd
= -1;
3517 cpu_boost_id(false, CPUBOOSTID_TAGCACHE
);
3521 /* Commit changes to the database. */
3524 remove(TAGCACHE_FILE_TEMP
);
3525 logf("tagcache built!");
3528 #ifdef HAVE_TC_RAMCACHE
3531 /* Import runtime statistics if we just initialized the db. */
3532 if (hdr
->h
.serial
== 0)
3533 queue_post(&tagcache_queue
, Q_IMPORT_CHANGELOG
, 0);
3537 cpu_boost_id(false, CPUBOOSTID_TAGCACHE
);
3540 #ifdef HAVE_TC_RAMCACHE
3541 static void load_ramcache(void)
3546 cpu_boost_id(true, CPUBOOSTID_TAGCACHE
);
3548 /* At first we should load the cache (if exists). */
3549 stat
.ramcache
= load_tagcache();
3553 /* If loading failed, it must indicate some problem with the db
3554 * so disable it entirely to prevent further issues. */
3559 cpu_boost_id(false, CPUBOOSTID_TAGCACHE
);
3562 void tagcache_unload_ramcache(void)
3564 stat
.ramcache
= false;
3565 /* Just to make sure there is no statefile present. */
3566 // remove(TAGCACHE_STATEFILE);
3570 static bool check_all_headers(void)
3572 struct master_header myhdr
;
3573 struct tagcache_header tch
;
3577 if ( (fd
= open_master_fd(&myhdr
, false)) < 0)
3581 current_serial
= myhdr
.serial
;
3583 for (tag
= 0; tag
< TAG_COUNT
; tag
++)
3585 if (tagcache_is_numeric_tag(tag
))
3588 if ( (fd
= open_tag_fd(&tch
, tag
, false)) < 0)
3597 static void tagcache_thread(void)
3600 bool check_done
= false;
3602 /* If the previous cache build/update was interrupted, commit
3603 * the changes first in foreground. */
3604 cpu_boost_id(true, CPUBOOSTID_TAGCACHE
);
3609 #ifdef HAVE_TC_RAMCACHE
3610 # ifdef HAVE_EEPROM_SETTINGS
3611 if (firmware_settings
.initialized
&& firmware_settings
.disk_clean
)
3612 check_done
= tagcache_dumpload();
3614 remove(TAGCACHE_STATEFILE
);
3617 /* Allocate space for the tagcache if found on disk. */
3618 if (global_settings
.tagcache_ram
&& !stat
.ramcache
)
3619 allocate_tagcache();
3622 cpu_boost_id(false, CPUBOOSTID_TAGCACHE
);
3623 stat
.initialized
= true;
3625 /* Don't delay bootup with the header check but do it on background. */
3627 stat
.ready
= check_all_headers();
3631 queue_wait_w_tmo(&tagcache_queue
, &ev
, HZ
);
3635 case Q_IMPORT_CHANGELOG
:
3636 tagcache_import_changelog();
3646 #ifdef HAVE_TC_RAMCACHE
3649 check_deleted_files();
3655 if (check_done
|| !stat
.ready
)
3658 #ifdef HAVE_TC_RAMCACHE
3659 if (!stat
.ramcache
&& global_settings
.tagcache_ram
)
3662 if (stat
.ramcache
&& global_settings
.tagcache_autoupdate
)
3667 if (global_settings
.tagcache_autoupdate
)
3670 /* Don't do auto removal without dircache (very slow). */
3671 #ifdef HAVE_DIRCACHE
3672 if (dircache_is_enabled())
3673 check_deleted_files();
3677 logf("tagcache check done");
3689 case SYS_USB_CONNECTED
:
3690 logf("USB: TagCache");
3691 usb_acknowledge(SYS_USB_CONNECTED_ACK
);
3692 usb_wait_for_disconnect(&tagcache_queue
);
3699 bool tagcache_prepare_shutdown(void)
3701 if (tagcache_get_commit_step() > 0)
3704 tagcache_stop_scan();
3705 while (read_lock
|| write_lock
)
3711 void tagcache_shutdown(void)
3713 #ifdef HAVE_EEPROM_SETTINGS
3715 tagcache_dumpsave();
3719 static int get_progress(void)
3721 int total_count
= -1;
3723 #ifdef HAVE_DIRCACHE
3724 if (dircache_is_enabled())
3726 total_count
= dircache_get_entry_count();
3730 #ifdef HAVE_TC_RAMCACHE
3732 if (hdr
&& stat
.ramcache
)
3733 total_count
= hdr
->h
.tch
.entry_count
;
3737 if (total_count
< 0)
3740 return processed_dir_count
* 100 / total_count
;
3743 struct tagcache_stat
* tagcache_get_stat(void)
3745 stat
.progress
= get_progress();
3746 stat
.processed_entries
= processed_dir_count
;
3751 void tagcache_start_scan(void)
3753 queue_post(&tagcache_queue
, Q_START_SCAN
, 0);
3756 bool tagcache_update(void)
3761 queue_post(&tagcache_queue
, Q_UPDATE
, 0);
3762 gui_syncsplash(HZ
*2, true, str(LANG_TAGCACHE_FORCE_UPDATE_SPLASH
));
3767 bool tagcache_rebuild(void)
3769 queue_post(&tagcache_queue
, Q_REBUILD
, 0);
3770 gui_syncsplash(HZ
*2, true, str(LANG_TAGCACHE_FORCE_UPDATE_SPLASH
));
3775 void tagcache_stop_scan(void)
3777 queue_post(&tagcache_queue
, Q_STOP_SCAN
, 0);
3780 #ifdef HAVE_TC_RAMCACHE
3781 bool tagcache_is_ramcache(void)
3783 return stat
.ramcache
;
3788 void tagcache_init(void)
3790 memset(&stat
, 0, sizeof(struct tagcache_stat
));
3791 filenametag_fd
= -1;
3793 write_lock
= read_lock
= 0;
3795 queue_init(&tagcache_queue
, true);
3796 create_thread(tagcache_thread
, tagcache_stack
,
3797 sizeof(tagcache_stack
), tagcache_thread_name
3798 IF_PRIO(, PRIORITY_BACKGROUND
));
3801 bool tagcache_is_initialized(void)
3803 return stat
.initialized
;
3806 int tagcache_get_commit_step(void)
3808 return stat
.commit_step
;