epg: more robust 32-bit object ID handling
Changes: - store the last id number to the epgdb file (the purpose is to reuse the id numbers as last as possible) - use RB tree to track id numbers (faster lookups) - skip invalid zero IDs (++_epg_object_idx) - skip used IDs (rare, but possible) The goal is to handle correctly the 32-bit ID wrapping (++_epg_object_idx).
This commit is contained in:
parent
54de3dbc79
commit
1fbbbb8e6a
4 changed files with 84 additions and 21 deletions
57
src/epg.c
57
src/epg.c
|
@ -39,6 +39,9 @@
|
|||
#define EPG_HASH_WIDTH 1024
|
||||
#define EPG_HASH_MASK (EPG_HASH_WIDTH - 1)
|
||||
|
||||
/* Objects tree */
|
||||
epg_object_tree_t epg_objects[EPG_HASH_WIDTH];
|
||||
|
||||
/* URI lists */
|
||||
epg_object_tree_t epg_brands;
|
||||
epg_object_tree_t epg_seasons;
|
||||
|
@ -46,17 +49,29 @@ epg_object_tree_t epg_episodes;
|
|||
epg_object_tree_t epg_serieslinks;
|
||||
|
||||
/* Other special case lists */
|
||||
epg_object_list_t epg_objects[EPG_HASH_WIDTH];
|
||||
epg_object_list_t epg_object_unref;
|
||||
epg_object_list_t epg_object_updated;
|
||||
|
||||
/* Global counter */
|
||||
static uint32_t _epg_object_idx = 0;
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
static inline epg_object_tree_t *epg_id_tree( epg_object_t *eo )
|
||||
{
|
||||
return &epg_objects[eo->id & EPG_HASH_MASK];
|
||||
}
|
||||
|
||||
/* **************************************************************************
|
||||
* Comparators / Ordering
|
||||
* *************************************************************************/
|
||||
|
||||
static int _id_cmp ( const void *a, const void *b )
|
||||
{
|
||||
return ((epg_object_t*)a)->id - ((epg_object_t*)b)->id;
|
||||
}
|
||||
|
||||
static int _uri_cmp ( const void *a, const void *b )
|
||||
{
|
||||
return strcmp(((epg_object_t*)a)->uri, ((epg_object_t*)b)->uri);
|
||||
|
@ -132,7 +147,7 @@ static void _epg_object_destroy
|
|||
if (eo->uri) free(eo->uri);
|
||||
if (tree) RB_REMOVE(tree, eo, uri_link);
|
||||
if (eo->_updated) LIST_REMOVE(eo, up_link);
|
||||
LIST_REMOVE(eo, id_link);
|
||||
RB_REMOVE(epg_id_tree(eo), eo, id_link);
|
||||
}
|
||||
|
||||
static void _epg_object_getref ( void *o )
|
||||
|
@ -169,15 +184,25 @@ static void _epg_object_set_updated ( void *o )
|
|||
static void _epg_object_create ( void *o )
|
||||
{
|
||||
epg_object_t *eo = o;
|
||||
uint32_t id = eo->id;
|
||||
if (!id) eo->id = ++_epg_object_idx;
|
||||
if (!eo->id) eo->id = ++_epg_object_idx;
|
||||
else if (eo->id > _epg_object_idx) _epg_object_idx = eo->id;
|
||||
if (!eo->getref) eo->getref = _epg_object_getref;
|
||||
if (!eo->putref) eo->putref = _epg_object_putref;
|
||||
tvhtrace("epg", "eo [%p, %u, %d, %s] created",
|
||||
eo, eo->id, eo->type, eo->uri);
|
||||
_epg_object_set_updated(eo);
|
||||
LIST_INSERT_HEAD(&epg_object_unref, eo, un_link);
|
||||
LIST_INSERT_HEAD(&epg_objects[eo->id & EPG_HASH_MASK], eo, id_link);
|
||||
while (1) {
|
||||
if (!RB_INSERT_SORTED(epg_id_tree(eo), eo, id_link, _id_cmp))
|
||||
break;
|
||||
if (id) {
|
||||
tvherror("epg", "fatal error, duplicate EPG ID");
|
||||
abort();
|
||||
}
|
||||
eo->id = ++_epg_object_idx;
|
||||
if (!eo->id) eo->id = ++_epg_object_idx;
|
||||
}
|
||||
}
|
||||
|
||||
static epg_object_t *_epg_object_find_by_uri
|
||||
|
@ -211,11 +236,11 @@ static epg_object_t *_epg_object_find_by_uri
|
|||
|
||||
epg_object_t *epg_object_find_by_id ( uint32_t id, epg_object_type_t type )
|
||||
{
|
||||
epg_object_t *eo;
|
||||
LIST_FOREACH(eo, &epg_objects[id & EPG_HASH_MASK], id_link) {
|
||||
if (eo->id == id)
|
||||
return ((type == EPG_UNDEF) || (eo->type == type)) ? eo : NULL;
|
||||
}
|
||||
epg_object_t *eo, temp;
|
||||
temp.id = id;
|
||||
eo = RB_FIND(epg_id_tree(&temp), &temp, id_link, _id_cmp);
|
||||
if (eo && eo->type == type)
|
||||
return eo;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -2603,6 +2628,20 @@ void epg_query_free(epg_query_t *eq)
|
|||
* Miscellaneous
|
||||
* *************************************************************************/
|
||||
|
||||
htsmsg_t *epg_config_serialize( void )
|
||||
{
|
||||
htsmsg_t *m = htsmsg_create_map();
|
||||
htsmsg_add_u32(m, "last_id", _epg_object_idx);
|
||||
return m;
|
||||
}
|
||||
|
||||
int epg_config_deserialize( htsmsg_t *m )
|
||||
{
|
||||
if (htsmsg_get_u32(m, "last_id", &_epg_object_idx))
|
||||
return 0;
|
||||
return 1; /* ok */
|
||||
}
|
||||
|
||||
void epg_skel_done(void)
|
||||
{
|
||||
epg_object_t **skel;
|
||||
|
|
|
@ -104,7 +104,7 @@ typedef enum epg_object_type
|
|||
struct epg_object
|
||||
{
|
||||
RB_ENTRY(epg_object) uri_link; ///< Global URI link
|
||||
LIST_ENTRY(epg_object) id_link; ///< Global (ID) link
|
||||
RB_ENTRY(epg_object) id_link; ///< Global (ID) link
|
||||
LIST_ENTRY(epg_object) un_link; ///< Global unref'd link
|
||||
LIST_ENTRY(epg_object) up_link; ///< Global updated link
|
||||
|
||||
|
@ -527,6 +527,12 @@ epg_broadcast_t *epg_broadcast_deserialize
|
|||
/* Unlink */
|
||||
void epg_channel_unlink ( struct channel *ch );
|
||||
|
||||
/* ************************************************************************
|
||||
* Global config
|
||||
* ***********************************************************************/
|
||||
htsmsg_t *epg_config_serialize ( void );
|
||||
int epg_config_deserialize ( htsmsg_t *m );
|
||||
|
||||
/* ************************************************************************
|
||||
* Querying
|
||||
* ***********************************************************************/
|
||||
|
|
39
src/epgdb.c
39
src/epgdb.c
|
@ -131,6 +131,10 @@ _epgdb_v2_process( char **sect, htsmsg_t *m, epggrab_stats_t *stats )
|
|||
} else if ( !strcmp(*sect, "broadcasts") ) {
|
||||
if (epg_broadcast_deserialize(m, 1, &save)) stats->broadcasts.total++;
|
||||
|
||||
/* Global config */
|
||||
} else if ( !strcmp(*sect, "config") ) {
|
||||
if (epg_config_deserialize(m)) stats->config.total++;
|
||||
|
||||
/* Unknown */
|
||||
} else {
|
||||
tvhlog(LOG_DEBUG, "epgdb", "malformed database section [%s]", *sect);
|
||||
|
@ -220,8 +224,17 @@ void epg_init ( void )
|
|||
|
||||
free(sect);
|
||||
|
||||
if (!stats.config.total) {
|
||||
htsmsg_t *m = htsmsg_create_map();
|
||||
/* it's not correct, but at least something */
|
||||
htsmsg_add_u32(m, "last_id", 64 * 1024 * 1024);
|
||||
if (!epg_config_deserialize(m))
|
||||
assert(0);
|
||||
}
|
||||
|
||||
/* Stats */
|
||||
tvhlog(LOG_INFO, "epgdb", "loaded v%d", ver);
|
||||
tvhlog(LOG_INFO, "epgdb", " config %d", stats.config.total);
|
||||
tvhlog(LOG_INFO, "epgdb", " channels %d", stats.channels.total);
|
||||
tvhlog(LOG_INFO, "epgdb", " brands %d", stats.brands.total);
|
||||
tvhlog(LOG_INFO, "epgdb", " seasons %d", stats.seasons.total);
|
||||
|
@ -300,31 +313,32 @@ void epg_save ( void )
|
|||
return;
|
||||
|
||||
memset(&stats, 0, sizeof(stats));
|
||||
if ( _epg_write_sect(fd, "brands") ) return;
|
||||
if ( _epg_write_sect(fd, "config") ) goto fin;
|
||||
if (_epg_write(fd, epg_config_serialize())) goto fin;
|
||||
if ( _epg_write_sect(fd, "brands") ) goto fin;
|
||||
RB_FOREACH(eo, &epg_brands, uri_link) {
|
||||
if (_epg_write(fd, epg_brand_serialize((epg_brand_t*)eo))) return;
|
||||
if (_epg_write(fd, epg_brand_serialize((epg_brand_t*)eo))) goto fin;
|
||||
stats.brands.total++;
|
||||
}
|
||||
if ( _epg_write_sect(fd, "seasons") ) return;
|
||||
if ( _epg_write_sect(fd, "seasons") ) goto fin;
|
||||
RB_FOREACH(eo, &epg_seasons, uri_link) {
|
||||
if (_epg_write(fd, epg_season_serialize((epg_season_t*)eo))) return;
|
||||
if (_epg_write(fd, epg_season_serialize((epg_season_t*)eo))) goto fin;
|
||||
stats.seasons.total++;
|
||||
}
|
||||
if ( _epg_write_sect(fd, "episodes") ) return;
|
||||
if ( _epg_write_sect(fd, "episodes") ) goto fin;
|
||||
RB_FOREACH(eo, &epg_episodes, uri_link) {
|
||||
if (_epg_write(fd, epg_episode_serialize((epg_episode_t*)eo))) return;
|
||||
if (_epg_write(fd, epg_episode_serialize((epg_episode_t*)eo))) goto fin;
|
||||
stats.episodes.total++;
|
||||
}
|
||||
if ( _epg_write_sect(fd, "serieslinks") ) return;
|
||||
if ( _epg_write_sect(fd, "serieslinks") ) goto fin;
|
||||
RB_FOREACH(eo, &epg_serieslinks, uri_link) {
|
||||
if (_epg_write(fd, epg_serieslink_serialize((epg_serieslink_t*)eo)))
|
||||
return;
|
||||
if (_epg_write(fd, epg_serieslink_serialize((epg_serieslink_t*)eo))) goto fin;
|
||||
stats.seasons.total++;
|
||||
}
|
||||
if ( _epg_write_sect(fd, "broadcasts") ) return;
|
||||
if ( _epg_write_sect(fd, "broadcasts") ) goto fin;
|
||||
CHANNEL_FOREACH(ch) {
|
||||
RB_FOREACH(ebc, &ch->ch_epg_schedule, sched_link) {
|
||||
if (_epg_write(fd, epg_broadcast_serialize(ebc))) return;
|
||||
if (_epg_write(fd, epg_broadcast_serialize(ebc))) goto fin;
|
||||
stats.broadcasts.total++;
|
||||
}
|
||||
}
|
||||
|
@ -335,4 +349,7 @@ void epg_save ( void )
|
|||
tvhlog(LOG_INFO, "epgdb", " seasons %d", stats.seasons.total);
|
||||
tvhlog(LOG_INFO, "epgdb", " episodes %d", stats.episodes.total);
|
||||
tvhlog(LOG_INFO, "epgdb", " broadcasts %d", stats.broadcasts.total);
|
||||
|
||||
fin:
|
||||
close(fd);
|
||||
}
|
||||
|
|
|
@ -58,6 +58,7 @@ typedef struct epggrab_stats
|
|||
epggrab_stats_part_t seasons;
|
||||
epggrab_stats_part_t episodes;
|
||||
epggrab_stats_part_t broadcasts;
|
||||
epggrab_stats_part_t config;
|
||||
} epggrab_stats_t;
|
||||
|
||||
/* **************************************************************************
|
||||
|
|
Loading…
Add table
Reference in a new issue