]> git.sesse.net Git - vlc/commitdiff
Use a hash for stats. Not finished
authorClément Stenac <zorglub@videolan.org>
Fri, 3 Feb 2006 23:52:45 +0000 (23:52 +0000)
committerClément Stenac <zorglub@videolan.org>
Fri, 3 Feb 2006 23:52:45 +0000 (23:52 +0000)
Makefile.am
include/vlc_common.h
include/vlc_messages.h
include/vlc_symbols.h
src/misc/hashtables.c [new file with mode: 0644]
src/misc/stats.c

index 0c36d110564b8732fcfb53e819938997f0c1c245..139dba4f9c0e0d2d0abad6033ca1d73f9433194d 100644 (file)
@@ -470,6 +470,7 @@ SOURCES_libvlc_common = \
        src/misc/update.c \
        src/misc/vlm.c \
        src/misc/xml.c \
+       src/misc/hashtables.c \
        src/misc/version.c \
        src/extras/libc.c \
        src/control/core.c \
index 3f16671f93a89ebfe85d0cc8f3d217da96588a01..7daebe4ae7810b98fa4fc404279e5611c88de256 100644 (file)
@@ -202,6 +202,7 @@ typedef struct libvlc_t libvlc_t;
 typedef struct vlc_t vlc_t;
 typedef struct variable_t variable_t;
 typedef struct date_t date_t;
+typedef struct hashtable_entry_t hashtable_entry_t;
 
 /* Messages */
 typedef struct msg_bank_t msg_bank_t;
@@ -642,6 +643,20 @@ static int64_t GCD( int64_t a, int64_t b )
         }                                       \
     }
 
+/* Hash tables handling */
+struct hashtable_entry_t
+{
+    int       i_id;
+    char     *psz_name;
+    uint64_t  i_hash;
+    void     *p_data;
+};
+
+VLC_EXPORT( void, vlc_HashInsert, (hashtable_entry_t **, int *, int, const char *, void *));
+VLC_EXPORT( void*, vlc_HashRetrieve, (hashtable_entry_t*, int, int, const char *) );
+VLC_EXPORT( int, vlc_HashLookup, (hashtable_entry_t *, int, int, const char *) );
+
+
 /* MSB (big endian)/LSB (little endian) conversions - network order is always
  * MSB, and should be used for both network communications and files. Note that
  * byte orders other than little and big endians are not supported, but only
index 6b0343e12352ecae7c9d1ee323fa772a16d26915..b1f2fbd6947c7066e150c8a237770d185a75f60f 100644 (file)
@@ -243,8 +243,8 @@ struct stats_handler_t
 {
     VLC_COMMON_MEMBERS
 
-    int         i_counters;
-    counter_t **pp_counters;
+    int                 i_counters;
+    hashtable_entry_t * p_counters;
 };
 
 VLC_EXPORT( void, stats_HandlerDestroy, (stats_handler_t*) );
index 190850f47e0918e4b2da656717cc7f83c8d4c641..e22d64f162442ae3035f757f862d147e3220a258 100644 (file)
@@ -308,6 +308,7 @@ int playlist_ItemAdd (playlist_t *, playlist_item_t *, int, int);
 aout_buffer_t * aout_OutputNextBuffer (aout_instance_t *, mtime_t, vlc_bool_t);
 int playlist_LockReplace (playlist_t *,playlist_item_t *, input_item_t*);
 int __intf_Eject (vlc_object_t *, const char *);
+void vlc_HashInsert (hashtable_entry_t **, int *, int, const char *, void *);
 int input_Control (input_thread_t *, int i_query, ...);
 int __aout_VolumeUp (vlc_object_t *, int, audio_volume_t *);
 void osd_Message (spu_t *, int, char *, ...);
@@ -316,6 +317,7 @@ void __osd_MenuUp (vlc_object_t *);
 int __aout_VolumeDown (vlc_object_t *, int, audio_volume_t *);
 sout_instance_t * __sout_NewInstance (vlc_object_t *, char *);
 subpicture_t * spu_CreateSubpicture (spu_t *);
+int vlc_HashLookup (hashtable_entry_t *, int, int, const char *);
 void httpd_MsgAdd (httpd_message_t *, char *psz_name, char *psz_value, ...);
 int vout_vaControlDefault (vout_thread_t *, int, va_list);
 int playlist_NodeEmpty (playlist_t *, playlist_item_t *, vlc_bool_t);
@@ -359,6 +361,7 @@ int intf_RunThread (intf_thread_t *);
 int httpd_StreamSend (httpd_stream_t *, uint8_t *p_data, int i_data);
 decoder_t * input_DecoderNew (input_thread_t *, es_format_t *, vlc_bool_t b_force_decoder);
 xml_t * __xml_Create (vlc_object_t *);
+void* vlc_HashRetrieve (hashtable_entry_t*, int, int, const char *);
 msg_subscription_t* __msg_Subscribe (vlc_object_t *, int);
 const char * VLC_Version (void);
 session_descriptor_t* sout_AnnounceRegisterSDP (sout_instance_t *,const char *, const char *, announce_method_t*);
@@ -919,6 +922,9 @@ struct module_symbols_t
     unsigned int (*update_iterator_ChooseMirrorAndFile_inner) (update_iterator_t *, int, int, int);
     update_t * (*__update_New_inner) (vlc_object_t *);
     void (*update_download_inner) (update_iterator_t *, char *);
+    void (*vlc_HashInsert_inner) (hashtable_entry_t **, int *, int, const char *, void *);
+    int (*vlc_HashLookup_inner) (hashtable_entry_t *, int, int, const char *);
+    void* (*vlc_HashRetrieve_inner) (hashtable_entry_t*, int, int, const char *);
 };
 #  if defined (__PLUGIN__)
 #  define aout_FiltersCreatePipeline (p_symbols)->aout_FiltersCreatePipeline_inner
@@ -1363,6 +1369,9 @@ struct module_symbols_t
 #  define update_iterator_ChooseMirrorAndFile (p_symbols)->update_iterator_ChooseMirrorAndFile_inner
 #  define __update_New (p_symbols)->__update_New_inner
 #  define update_download (p_symbols)->update_download_inner
+#  define vlc_HashInsert (p_symbols)->vlc_HashInsert_inner
+#  define vlc_HashLookup (p_symbols)->vlc_HashLookup_inner
+#  define vlc_HashRetrieve (p_symbols)->vlc_HashRetrieve_inner
 #  elif defined (HAVE_DYNAMIC_PLUGINS) && !defined (__BUILTIN__)
 /******************************************************************
  * STORE_SYMBOLS: store VLC APIs into p_symbols for plugin access.
@@ -1810,6 +1819,9 @@ struct module_symbols_t
     ((p_symbols)->update_iterator_ChooseMirrorAndFile_inner) = update_iterator_ChooseMirrorAndFile; \
     ((p_symbols)->__update_New_inner) = __update_New; \
     ((p_symbols)->update_download_inner) = update_download; \
+    ((p_symbols)->vlc_HashInsert_inner) = vlc_HashInsert; \
+    ((p_symbols)->vlc_HashLookup_inner) = vlc_HashLookup; \
+    ((p_symbols)->vlc_HashRetrieve_inner) = vlc_HashRetrieve; \
     (p_symbols)->net_ConvertIPv4_deprecated = NULL; \
     (p_symbols)->__stats_CounterGet_deprecated = NULL; \
     (p_symbols)->__stats_TimerDumpAll_deprecated = NULL; \
diff --git a/src/misc/hashtables.c b/src/misc/hashtables.c
new file mode 100644 (file)
index 0000000..3e24c9a
--- /dev/null
@@ -0,0 +1,223 @@
+/*****************************************************************************
+ * hashtables.c: Hash tables handling
+ *****************************************************************************
+ * Copyright (C) 2002-2004 the VideoLAN team
+ * $Id: variables.c 13991 2006-01-22 17:12:24Z zorglub $
+ *
+ * Authors: Samuel Hocevar <sam@zoy.org>
+ *          Clément Stenac <zorglub@videolan.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Preamble
+ *****************************************************************************/
+#include <vlc/vlc.h>
+
+static uint64_t HashString    ( const char *, int );
+static int      FindSlot      ( hashtable_entry_t *, int, uint64_t );
+static int      LookupInner   ( hashtable_entry_t *, int, uint64_t );
+
+
+
+void vlc_HashInsert( hashtable_entry_t **pp_array, int *pi_count, int i_id,
+                     const char *psz_name, void *p_data )
+{
+    hashtable_entry_t *p_new;
+    int64_t i_hash = HashString( psz_name, i_id );
+    int i_new = FindSlot( *pp_array, *pi_count, i_hash );
+
+    *pp_array = realloc( *pp_array, ( *pi_count + 2) * sizeof( hashtable_entry_t ) );
+
+    memmove( *pp_array + i_new + 1 , *pp_array + i_new, ( *pi_count - i_new ) *
+                sizeof( hashtable_entry_t ) );
+    (*pi_count)++;
+
+    p_new = &((*pp_array)[i_new]);
+    p_new->i_hash = i_hash;
+    p_new->i_id = i_id;
+    p_new->psz_name = strdup( psz_name );
+    p_new->p_data = p_data;
+
+}
+
+void * vlc_HashRetrieve( hashtable_entry_t *p_array, int i_count, int i_id,
+                         const char *psz_name )
+{
+    int i_new = vlc_HashLookup( p_array, i_count, i_id, psz_name );
+
+    if( i_new >=  0 && i_new < i_count )
+    {
+        return p_array[i_new].p_data;
+    }
+    return NULL;
+}
+
+/*****************************************************************************
+ * FindSlot: find an empty slot to insert a new variable
+ *****************************************************************************
+ * We use a recursive inner function indexed on the hash. This function does
+ * nothing in the rare cases where a collision may occur, see Lookup()
+ * to see how we handle them.
+ * XXX: does this really need to be written recursively?
+ *****************************************************************************/
+static int FindSlot( hashtable_entry_t *p_array, int i_count, uint64_t i_hash )
+{
+    int i_middle;
+
+
+    if( i_hash <= p_array[0].i_hash )
+    {
+        return 0;
+    }
+
+    if( i_hash >= p_array[i_count - 1].i_hash )
+    {
+        return i_count;
+    }
+
+    i_middle = i_count / 2;
+
+    /* We know that 0 < i_middle */
+    if( i_hash < p_array[i_middle].i_hash )
+    {
+        return FindSlot( p_array, i_middle, i_hash );
+    }
+
+    /* We know that i_middle + 1 < i_count */
+    if( i_hash > p_array[i_middle + 1].i_hash )
+    {
+        return i_middle + 1 + FindSlot( p_array + i_middle + 1,
+                                        i_count - i_middle - 1,
+                                        i_hash );
+    }
+
+    return i_middle + 1;
+}
+
+/*****************************************************************************
+ * Lookup: find an existing variable given its name and id
+ *****************************************************************************
+ * We use a recursive inner function indexed on the hash. Care is taken of
+ * possible hash collisions.
+ * XXX: does this really need to be written recursively?
+ *****************************************************************************/
+int vlc_HashLookup( hashtable_entry_t *p_array, int i_count,
+                    int i_id, const char *psz_name )
+{
+    uint64_t i_hash;
+    int i, i_pos;
+
+    if( i_count == 0 )
+    {
+        return -1;
+    }
+
+    i_hash = HashString( psz_name, i_id );
+
+    i_pos = LookupInner( p_array, i_count, i_hash );
+
+    /* Hash not found */
+    if( i_hash != p_array[i_pos].i_hash )
+    {
+        return -1;
+    }
+
+    /* Hash found, entry found */
+    if( !strcmp( psz_name, p_array[i_pos].psz_name ) )
+    {
+        return i_pos;
+    }
+
+    /* Hash collision! This should be very rare, but we cannot guarantee
+     * it will never happen. Just do an exhaustive search amongst all
+     * entries with the same hash. */
+    for( i = i_pos - 1 ; i > 0 && i_hash == p_array[i].i_hash ; i-- )
+    {
+        if( !strcmp( psz_name, p_array[i].psz_name ) && p_array[i].i_id == i_id )
+        {
+            return i;
+        }
+    }
+
+    for( i = i_pos + 1 ; i < i_count && i_hash == p_array[i].i_hash ; i++ )
+    {
+        if( !strcmp( psz_name, p_array[i].psz_name ) && p_array[i].i_id == i_id )
+        {
+            return i;
+        }
+    }
+
+    /* Hash found, but entry not found */
+    return -1;
+}
+
+static int LookupInner( hashtable_entry_t *p_array, int i_count, uint64_t i_hash )
+{
+    int i_middle;
+
+    if( i_hash <= p_array[0].i_hash )
+    {
+        return 0;
+    }
+
+    if( i_hash >= p_array[i_count-1].i_hash )
+    {
+        return i_count - 1;
+    }
+
+    i_middle = i_count / 2;
+
+    /* We know that 0 < i_middle */
+    if( i_hash < p_array[i_middle].i_hash )
+    {
+        return LookupInner( p_array, i_middle, i_hash );
+    }
+
+    /* We know that i_middle + 1 < i_count */
+    if( i_hash > p_array[i_middle].i_hash )
+    {
+        return i_middle + LookupInner( p_array + i_middle,
+                                       i_count - i_middle,
+                                       i_hash );
+    }
+
+    return i_middle;
+}
+
+
+/*****************************************************************************
+ * HashString: our cool hash function
+ *****************************************************************************
+ * This function is not intended to be crypto-secure, we only want it to be
+ * fast and not suck too much. This one is pretty fast and did 0 collisions
+ * in wenglish's dictionary.
+ *****************************************************************************/
+static uint64_t HashString( const char *psz_string, int i_id )
+{
+    uint64_t i_hash = 0;
+
+    while( *psz_string )
+    {
+        i_hash += *psz_string++;
+        i_hash += i_hash << 10;
+        i_hash ^= i_hash >> 8;
+    }
+
+    i_hash += ( i_id << 32 );
+
+    return i_hash;
+}
index 8d919e40cc15c10e0241f1d877371e805246c0f6..fe04bb154d638ad3007df882e9558b23ba610a49 100644 (file)
@@ -57,7 +57,8 @@ void stats_HandlerDestroy( stats_handler_t *p_stats )
     for ( i =  p_stats->i_counters - 1 ; i >= 0 ; i-- )
     {
         int j;
-        counter_t * p_counter = p_stats->pp_counters[i];
+        hashtable_entry_t p_entry = p_stats->p_counters[i];
+        counter_t * p_counter = p_entry.p_data;
 
         for( j = p_counter->i_samples -1; j >= 0 ; j-- )
         {
@@ -66,7 +67,8 @@ void stats_HandlerDestroy( stats_handler_t *p_stats )
             free( p_sample );
         }
         free( p_counter->psz_name );
-        REMOVE_ELEM( p_stats->pp_counters, p_stats->i_counters, i );
+        free( p_entry.psz_name );
+        REMOVE_ELEM( p_stats->p_counters, p_stats->i_counters, i );
         free( p_counter );
     }
 }
@@ -109,10 +111,8 @@ int __stats_Create( vlc_object_t *p_this, const char *psz_name, int i_type,
     p_counter->update_interval = 0;
     p_counter->last_update = 0;
 
-    INSERT_ELEM( p_handler->pp_counters,
-                 p_handler->i_counters,
-                 p_handler->i_counters,
-                 p_counter );
+    vlc_HashInsert( &p_handler->p_counters, &p_handler->i_counters, p_this->i_object_id,
+                    psz_name, p_counter );
 
     vlc_mutex_unlock( &p_handler->object_lock );
 
@@ -467,9 +467,10 @@ void __stats_TimersDumpAll( vlc_object_t *p_obj )
     vlc_mutex_lock( &p_handler->object_lock );
     for ( i = 0 ; i< p_handler->i_counters; i++ )
     {
-        if( p_handler->pp_counters[i]->i_compute_type == STATS_TIMER )
+        counter_t * p_counter = (counter_t *)(p_handler->p_counters[i].p_data);
+        if( p_counter->i_compute_type == STATS_TIMER )
         {
-            TimerDump( p_obj, p_handler->pp_counters[i], VLC_FALSE );
+            TimerDump( p_obj, p_counter, VLC_FALSE );
         }
     }
     vlc_mutex_unlock( &p_handler->object_lock );
@@ -613,16 +614,8 @@ static counter_t *GetCounter( stats_handler_t *p_handler, int i_object_id,
                               const char *psz_name )
 {
     int i;
-   for( i = 0; i< p_handler->i_counters; i++ )
-    {
-        counter_t *p_counter = p_handler->pp_counters[i];
-        if( p_counter->i_source_object == i_object_id &&
-            !strcmp( p_counter->psz_name, psz_name ) )
-        {
-            return p_counter;
-        }
-    }
-    return NULL;
+    return (counter_t *)vlc_HashRetrieve( p_handler->p_counters, p_handler->i_counters,
+                                          i_object_id, psz_name );
 }
 
 
@@ -664,7 +657,7 @@ static stats_handler_t* stats_HandlerCreate( vlc_object_t *p_this )
         return NULL;
     }
     p_handler->i_counters = 0;
-    p_handler->pp_counters = NULL;
+    p_handler->p_counters = (hashtable_entry_t *) malloc( 5 * sizeof( variable_t ) );
 
     /// \bug is it p_vlc or p_libvlc ?
     vlc_object_attach( p_handler, p_this->p_vlc );