From 23d27c9c9cec127459721c3b9420eb349109b477 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Cl=C3=A9ment=20Stenac?= Date: Sat, 19 Aug 2006 16:19:31 +0000 Subject: [PATCH] * B-search macro * Redo dictionnary handling to remove recursion and bugs --- include/vlc_common.h | 42 ++++++-- include/vlc_symbols.h | 30 ++++-- src/Makefile.am | 2 +- src/input/item.c | 2 +- src/misc/dict.c | 163 ++++++++++++++++++++++++++++++ src/misc/hashtables.c | 223 ------------------------------------------ 6 files changed, 220 insertions(+), 242 deletions(-) create mode 100644 src/misc/dict.c delete mode 100644 src/misc/hashtables.c diff --git a/include/vlc_common.h b/include/vlc_common.h index 2f3e156d2a..3be65bb5c4 100644 --- a/include/vlc_common.h +++ b/include/vlc_common.h @@ -203,7 +203,8 @@ 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; +typedef struct dict_entry_t dict_entry_t; +typedef struct dict_t dict_t; typedef struct gc_object_t gc_object_t ; /* Messages */ @@ -717,19 +718,44 @@ static int64_t GCD( int64_t a, int64_t b ) } \ } -/* Hash tables handling */ -struct hashtable_entry_t +/* Binary search in an array */ +#define BSEARCH( entries, count, elem, zetype, key, answer ) { \ + int low = 0, high = count - 1; \ + answer = -1; \ + while( low <= high ) {\ + int mid = (low + high ) / 2; /* Just don't care about 2^30 tables */ \ + zetype mid_val = entries[mid] elem;\ + if( mid_val < key ) \ + low = mid + 1; \ + else if ( mid_val > key ) \ + high = mid -1; \ + else \ + { \ + answer = mid; break; \ + }\ + } \ +} + +/* Dictionnary handling */ +struct dict_entry_t { - int i_id; - char *psz_name; + int i_int; + char *psz_string; 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 *) ); +struct dict_t +{ + dict_entry_t *p_entries; + int i_entries; +}; +VLC_EXPORT( dict_t *, vlc_DictNew, (void) ); +VLC_EXPORT( void, vlc_DictClear, (dict_t * ) ); +VLC_EXPORT( void, vlc_DictInsert, (dict_t *, int, const char *, void * ) ); +VLC_EXPORT( void*, vlc_DictGet, (dict_t *, int, const char * ) ); +VLC_EXPORT( int, vlc_DictLookup, (dict_t *, 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 diff --git a/include/vlc_symbols.h b/include/vlc_symbols.h index 09393102b1..a3659b5ab8 100644 --- a/include/vlc_symbols.h +++ b/include/vlc_symbols.h @@ -463,9 +463,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 *); + void *vlc_HashInsert_deprecated; + void *vlc_HashLookup_deprecated; + void *vlc_HashRetrieve_deprecated; void * (*utf8_opendir_inner) (const char *dirname); FILE * (*utf8_fopen_inner) (const char *filename, const char *mode); const char * (*utf8_readdir_inner) (void *dir); @@ -525,6 +525,11 @@ struct module_symbols_t int (*__intf_Progress_inner) (vlc_object_t*, const char*, const char*, float, int); void (*__intf_ProgressUpdate_inner) (vlc_object_t*, int, const char*, float, int); int (*playlist_AddInput_inner) (playlist_t *, input_item_t *,int , int, vlc_bool_t); + void (*vlc_DictInsert_inner) (dict_t *, int, const char *, void *); + void* (*vlc_DictGet_inner) (dict_t *, int, const char *); + int (*vlc_DictLookup_inner) (dict_t *, int, const char *); + void (*vlc_DictClear_inner) (dict_t *); + dict_t * (*vlc_DictNew_inner) (void); }; # if defined (__PLUGIN__) # define aout_FiltersCreatePipeline (p_symbols)->aout_FiltersCreatePipeline_inner @@ -936,9 +941,6 @@ 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 # define utf8_opendir (p_symbols)->utf8_opendir_inner # define utf8_fopen (p_symbols)->utf8_fopen_inner # define utf8_readdir (p_symbols)->utf8_readdir_inner @@ -988,6 +990,11 @@ struct module_symbols_t # define __intf_Progress (p_symbols)->__intf_Progress_inner # define __intf_ProgressUpdate (p_symbols)->__intf_ProgressUpdate_inner # define playlist_AddInput (p_symbols)->playlist_AddInput_inner +# define vlc_DictInsert (p_symbols)->vlc_DictInsert_inner +# define vlc_DictGet (p_symbols)->vlc_DictGet_inner +# define vlc_DictLookup (p_symbols)->vlc_DictLookup_inner +# define vlc_DictClear (p_symbols)->vlc_DictClear_inner +# define vlc_DictNew (p_symbols)->vlc_DictNew_inner # elif defined (HAVE_DYNAMIC_PLUGINS) && !defined (__BUILTIN__) /****************************************************************** * STORE_SYMBOLS: store VLC APIs into p_symbols for plugin access. @@ -1402,9 +1409,6 @@ 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)->utf8_opendir_inner) = utf8_opendir; \ ((p_symbols)->utf8_fopen_inner) = utf8_fopen; \ ((p_symbols)->utf8_readdir_inner) = utf8_readdir; \ @@ -1454,6 +1458,11 @@ struct module_symbols_t ((p_symbols)->__intf_Progress_inner) = __intf_Progress; \ ((p_symbols)->__intf_ProgressUpdate_inner) = __intf_ProgressUpdate; \ ((p_symbols)->playlist_AddInput_inner) = playlist_AddInput; \ + ((p_symbols)->vlc_DictInsert_inner) = vlc_DictInsert; \ + ((p_symbols)->vlc_DictGet_inner) = vlc_DictGet; \ + ((p_symbols)->vlc_DictLookup_inner) = vlc_DictLookup; \ + ((p_symbols)->vlc_DictClear_inner) = vlc_DictClear; \ + ((p_symbols)->vlc_DictNew_inner) = vlc_DictNew; \ (p_symbols)->net_ConvertIPv4_deprecated = NULL; \ (p_symbols)->__playlist_ItemNew_deprecated = NULL; \ (p_symbols)->__playlist_ItemCopy_deprecated = NULL; \ @@ -1489,6 +1498,9 @@ struct module_symbols_t (p_symbols)->__stats_CounterGet_deprecated = NULL; \ (p_symbols)->stats_HandlerDestroy_deprecated = NULL; \ (p_symbols)->__stats_TimerDumpAll_deprecated = NULL; \ + (p_symbols)->vlc_HashInsert_deprecated = NULL; \ + (p_symbols)->vlc_HashLookup_deprecated = NULL; \ + (p_symbols)->vlc_HashRetrieve_deprecated = NULL; \ (p_symbols)->playlist_ItemNewFromInput_deprecated = NULL; \ (p_symbols)->playlist_PlaylistAdd_deprecated = NULL; \ (p_symbols)->playlist_PlaylistAddExt_deprecated = NULL; \ diff --git a/src/Makefile.am b/src/Makefile.am index 942e4ed972..f68101a117 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -325,7 +325,7 @@ SOURCES_libvlc_common = \ misc/update.c \ misc/vlm.c \ misc/xml.c \ - misc/hashtables.c \ + misc/dict.c \ misc/devices.c \ extras/libc.c \ control/core.c \ diff --git a/src/input/item.c b/src/input/item.c index 870c274185..6443d5964f 100644 --- a/src/input/item.c +++ b/src/input/item.c @@ -70,7 +70,7 @@ char *vlc_input_item_GetInfo( input_item_t *p_i, static void vlc_input_item_Destroy ( gc_object_t *p_this ) { vlc_object_t *p_obj = (vlc_object_t *)p_this->p_destructor_arg; - int i, i_top, i_bottom; + int i; input_item_t *p_input = (input_item_t *) p_this; playlist_t *p_playlist = (playlist_t *)vlc_object_find( p_obj, diff --git a/src/misc/dict.c b/src/misc/dict.c new file mode 100644 index 0000000000..36b5c369af --- /dev/null +++ b/src/misc/dict.c @@ -0,0 +1,163 @@ +/***************************************************************************** + * dict.c: Dictionnary handling + ***************************************************************************** + * Copyright (C) 2002-2004 the VideoLAN team + * $Id: variables.c 13991 2006-01-22 17:12:24Z zorglub $ + * + * Authors: Samuel Hocevar + * Clément Stenac + * + * 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. + *****************************************************************************/ + +#include +#include + +/***************************************************************************** + * Associative array + ***************************************************************************** + * This is quite a weak implementation of an associative array. + * It hashes the key and stores the entry in a sorted array that gets + * reallocated to insert new entries (so, this is SLOW) + * Lookup is done by using binary search on the array + */ + +static uint64_t DoHash ( const char *, int ); + +#define DARRAY p_dict->p_entries +#define DSIZE p_dict->i_entries + +dict_t *vlc_DictNew() +{ + DECMALLOC_NULL( p_dict, dict_t ); + p_dict->i_entries = 0; + p_dict->p_entries = NULL; + return p_dict; +} + +void vlc_DictClear( dict_t *p_dict ) +{ + int i = 0; + for( i = 0 ; i< DSIZE; i++ ) + { + FREE( DARRAY[i].psz_string ); + } + free( DARRAY ); + free( p_dict ); +} + +void vlc_DictInsert( dict_t *p_dict, int i_int, const char *psz_string, + void *p_data ) +{ + uint64_t i_hash = DoHash( psz_string, i_int ); + int i_new; + + /* Find a free slot */ + if( DSIZE == 0 || i_hash <= DARRAY[0].i_hash ) + i_new = 0; + else if( i_hash >= DARRAY[DSIZE-1].i_hash ) + i_new = DSIZE; + else + { + int i_low = 0, i_high = DSIZE; + while( i_low != i_high ) + { + int i_mid = (i_low + i_high)/2; + if( DARRAY[i_mid].i_hash < i_hash ) + i_low = i_mid + 1; + if( DARRAY[i_mid].i_hash > i_hash ) + i_high = i_low + 1; + } + i_new = i_low; + } + DARRAY = realloc( DARRAY, (DSIZE + 1) * sizeof( dict_entry_t ) ); + DSIZE++; + + if( i_new != DSIZE -1 ) + memmove( DARRAY + i_new + 1 , DARRAY + i_new, ( DSIZE - i_new - 1 ) * + sizeof( dict_entry_t ) ); + DARRAY[i_new].i_hash = i_hash; + DARRAY[i_new].i_int = i_int; + if( psz_string ) + DARRAY[i_new].psz_string = strdup( psz_string ); + else + DARRAY[i_new].psz_string = NULL; + DARRAY[i_new].p_data = p_data; +} + +void * vlc_DictGet( dict_t *p_dict, int i_int, const char *psz_string ) +{ + int i_new = vlc_DictLookup( p_dict, i_int, psz_string ); + if( i_new >= 0 ) + return DARRAY[i_new].p_data; + return NULL; +} + +int vlc_DictLookup( dict_t *p_dict, int i_int, const char *psz_string ) +{ + uint64_t i_hash; + int i, i_pos; + if( DSIZE == 0 ) return -1; + + i_hash = DoHash( psz_string, i_int ); + BSEARCH( p_dict->p_entries, p_dict->i_entries, .i_hash, uint64_t, + i_hash, i_pos ); + if( i_pos == -1 ) return -1; + + /* Hash found, let's check it looks like the entry */ + if( !strcmp( psz_string, DARRAY[i_pos].psz_string ) ) + 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 == DARRAY[i].i_hash ; i-- ) + { + if( !strcmp( psz_string, DARRAY[i].psz_string ) && + DARRAY[i].i_int == i_int ) + return i; + } + for( i = i_pos + 1 ; i < DSIZE && i_hash == DARRAY[i].i_hash ; i++ ) + { + if( !strcmp( psz_string, DARRAY[i].psz_string ) && + DARRAY[i].i_int == i_int ) + return i; + } + /* Hash found, but entry not found (quite strange !) */ + assert( 0 ); + return -1; +} + +/***************************************************************************** + * DoHash: 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 DoHash( const char *psz_string, int i_int ) +{ + uint64_t i_hash = 0; + if( psz_string ) + { + while( *psz_string ) + { + i_hash += *psz_string++; + i_hash += i_hash << 10; + i_hash ^= i_hash >> 8; + } + } + return i_hash + ( (uint64_t)i_int << 32 ); +} diff --git a/src/misc/hashtables.c b/src/misc/hashtables.c deleted file mode 100644 index 1bb3164726..0000000000 --- a/src/misc/hashtables.c +++ /dev/null @@ -1,223 +0,0 @@ -/***************************************************************************** - * 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 - * Clément Stenac - * - * 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 - -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_count == 0 || 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, let's check it looks like the entry - * We don't check the whole entry, this could lead to bad surprises :( */ - if( psz_name[0] == p_array[i_pos].psz_name[0] ) - { - 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 += ( (uint64_t)i_id << 32 ); - - return i_hash; -} -- 2.39.5