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:
Jaroslav Kysela 2014-10-05 20:51:13 +02:00
parent 54de3dbc79
commit 1fbbbb8e6a
4 changed files with 84 additions and 21 deletions

View file

@ -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;

View file

@ -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
* ***********************************************************************/

View file

@ -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);
}

View file

@ -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;
/* **************************************************************************