1 /*****************************************************************************
2 * variables.c: routines for object variables handling
3 *****************************************************************************
4 * Copyright (C) 2002 VideoLAN
5 * $Id: variables.c,v 1.7 2002/10/16 19:39:42 sam Exp $
7 * Authors: Samuel Hocevar <sam@zoy.org>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
22 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
30 # include <stdlib.h> /* realloc() */
33 /*****************************************************************************
35 *****************************************************************************/
36 struct callback_entry_t
38 vlc_callback_t pf_callback;
42 /*****************************************************************************
44 *****************************************************************************/
45 static u32 HashString ( const char * );
46 static int Insert ( variable_t *, int, const char * );
47 static int InsertInner ( variable_t *, int, u32 );
48 static int Lookup ( variable_t *, int, const char * );
49 static int LookupInner ( variable_t *, int, u32 );
51 /*****************************************************************************
52 * var_Create: initialize a vlc variable
53 *****************************************************************************
54 * We hash the given string and insert it into the sorted list. The insertion
55 * may require slow memory copies, but think about what we gain in the log(n)
56 * lookup phase when setting/getting the variable value!
57 *****************************************************************************/
58 int __var_Create( vlc_object_t *p_this, const char *psz_name, int i_type )
63 vlc_mutex_lock( &p_this->var_lock );
65 /* FIXME: if the variable already exists, we don't duplicate it. But we
66 * duplicate the lookups. It's not that serious, but if anyone finds some
67 * time to rework Insert() so that only one lookup has to be done, feel
69 i_new = Lookup( p_this->p_vars, p_this->i_vars, psz_name );
73 /* If the types differ, variable creation failed. */
74 if( i_type != p_this->p_vars[i_new].i_type )
76 vlc_mutex_unlock( &p_this->var_lock );
80 p_this->p_vars[i_new].i_usage++;
81 vlc_mutex_unlock( &p_this->var_lock );
85 i_new = Insert( p_this->p_vars, p_this->i_vars, psz_name );
87 if( (p_this->i_vars & 15) == 15 )
89 p_this->p_vars = realloc( p_this->p_vars,
90 (p_this->i_vars+17) * sizeof(variable_t) );
93 memmove( p_this->p_vars + i_new + 1,
94 p_this->p_vars + i_new,
95 (p_this->i_vars - i_new) * sizeof(variable_t) );
99 p_var = &p_this->p_vars[i_new];
101 p_var->i_hash = HashString( psz_name );
102 p_var->psz_name = strdup( psz_name );
104 p_var->i_type = i_type;
105 memset( &p_var->val, 0, sizeof(vlc_value_t) );
109 p_var->i_entries = 0;
110 p_var->p_entries = NULL;
112 /* Always initialize the variable */
116 p_var->val.b_bool = VLC_FALSE;
118 case VLC_VAR_INTEGER:
119 p_var->val.i_int = 0;
124 p_var->val.psz_string = strdup( "" );
127 p_var->val.f_float = 0.0;
132 case VLC_VAR_ADDRESS:
133 case VLC_VAR_COMMAND:
134 p_var->val.p_address = NULL;
137 p_var->val.p_address = malloc( sizeof(vlc_mutex_t) );
138 vlc_mutex_init( p_this, (vlc_mutex_t*)p_var->val.p_address );
142 vlc_mutex_unlock( &p_this->var_lock );
147 /*****************************************************************************
148 * var_Destroy: destroy a vlc variable
149 *****************************************************************************
150 * Look for the variable and destroy it if it is found. As in var_Create we
151 * do a call to memmove() but we have performance counterparts elsewhere.
152 *****************************************************************************/
153 int __var_Destroy( vlc_object_t *p_this, const char *psz_name )
158 vlc_mutex_lock( &p_this->var_lock );
160 i_del = Lookup( p_this->p_vars, p_this->i_vars, psz_name );
164 vlc_mutex_unlock( &p_this->var_lock );
168 p_var = &p_this->p_vars[i_del];
170 if( p_var->i_usage > 1 )
173 vlc_mutex_unlock( &p_this->var_lock );
177 /* Free value if needed */
178 switch( p_var->i_type )
183 free( p_var->val.psz_string );
187 vlc_mutex_destroy( (vlc_mutex_t*)p_var->val.p_address );
188 free( p_var->val.p_address );
192 /* Free callbacks if needed */
193 if( p_var->p_entries )
195 free( p_var->p_entries );
198 free( p_var->psz_name );
200 memmove( p_this->p_vars + i_del,
201 p_this->p_vars + i_del + 1,
202 (p_this->i_vars - i_del - 1) * sizeof(variable_t) );
204 if( (p_this->i_vars & 15) == 0 )
206 p_this->p_vars = realloc( p_this->p_vars,
207 (p_this->i_vars) * sizeof( variable_t ) );
212 vlc_mutex_unlock( &p_this->var_lock );
217 /*****************************************************************************
218 * var_Type: request a variable's type, 0 if not found
219 *****************************************************************************
221 *****************************************************************************/
222 int __var_Type( vlc_object_t *p_this, const char *psz_name )
226 vlc_mutex_lock( &p_this->var_lock );
228 i_var = Lookup( p_this->p_vars, p_this->i_vars, psz_name );
232 vlc_mutex_unlock( &p_this->var_lock );
236 i_type = p_this->p_vars[i_var].i_type;
238 vlc_mutex_unlock( &p_this->var_lock );
243 /*****************************************************************************
244 * var_Set: set a variable's value
245 *****************************************************************************
247 *****************************************************************************/
248 int __var_Set( vlc_object_t *p_this, const char *psz_name, vlc_value_t val )
254 vlc_mutex_lock( &p_this->var_lock );
256 i_var = Lookup( p_this->p_vars, p_this->i_vars, psz_name );
260 vlc_mutex_unlock( &p_this->var_lock );
264 p_var = &p_this->p_vars[i_var];
266 /* Duplicate data if needed */
267 switch( p_var->i_type )
272 val.psz_string = strdup( val.psz_string );
276 /* Backup needed stuff */
279 /* Set the variable */
282 /* Deal with callbacks */
283 if( p_var->i_entries )
287 for( i = p_var->i_entries ; i-- ; )
289 /* FIXME: oooh, see the deadlocks! see the race conditions! */
290 p_var->p_entries[i].pf_callback( p_this, p_var->psz_name,
292 p_var->p_entries[i].p_data );
296 /* Free data if needed */
297 switch( p_var->i_type )
302 free( oldval.psz_string );
306 vlc_mutex_unlock( &p_this->var_lock );
311 /*****************************************************************************
312 * var_Get: get a variable's value
313 *****************************************************************************
315 *****************************************************************************/
316 int __var_Get( vlc_object_t *p_this, const char *psz_name, vlc_value_t *p_val )
321 vlc_mutex_lock( &p_this->var_lock );
323 i_var = Lookup( p_this->p_vars, p_this->i_vars, psz_name );
327 vlc_mutex_unlock( &p_this->var_lock );
331 p_var = &p_this->p_vars[i_var];
333 /* Some variables trigger special behaviour. */
334 switch( p_var->i_type )
336 case VLC_VAR_COMMAND:
337 if( p_var->val.p_address )
339 /* We need to save data before releasing the lock */
341 int (*pf_command) (vlc_object_t *, char *, char *) =
342 p_var->val.p_address;
343 char *psz_cmd = strdup( p_var->psz_name );
344 char *psz_arg = strdup( p_val->psz_string );
346 vlc_mutex_unlock( &p_this->var_lock );
348 i_ret = pf_command( p_this, psz_cmd, psz_arg );
357 /* Really set the variable */
360 /* Duplicate value if needed */
361 switch( p_var->i_type )
366 if( p_val->psz_string )
368 p_val->psz_string = strdup( p_val->psz_string );
373 vlc_mutex_unlock( &p_this->var_lock );
378 /*****************************************************************************
379 * var_AddCallback: register a callback in a variable
380 *****************************************************************************
381 * We store a function pointer pf_callback that will be called upon variable
382 * modification. p_data is a generic pointer that will be passed as additional
383 * argument to the callback function.
384 *****************************************************************************/
385 int __var_AddCallback( vlc_object_t *p_this, const char *psz_name,
386 vlc_callback_t pf_callback, void *p_data )
391 vlc_mutex_lock( &p_this->var_lock );
393 i_var = Lookup( p_this->p_vars, p_this->i_vars, psz_name );
396 vlc_mutex_unlock( &p_this->var_lock );
400 p_var = &p_this->p_vars[i_var];
402 i_entry = p_var->i_entries++;
406 p_var->p_entries = realloc( p_var->p_entries,
407 sizeof( callback_entry_t ) * p_var->i_entries );
411 p_var->p_entries = malloc( sizeof( callback_entry_t ) );
414 p_var->p_entries[ i_entry ].pf_callback = pf_callback;
415 p_var->p_entries[ i_entry ].p_data = p_data;
417 vlc_mutex_unlock( &p_this->var_lock );
422 /*****************************************************************************
423 * var_DelCallback: remove a callback from a variable
424 *****************************************************************************
425 * pf_callback and p_data have to be given again, because different objects
426 * might have registered the same callback function.
427 *****************************************************************************/
428 int __var_DelCallback( vlc_object_t *p_this, const char *psz_name,
429 vlc_callback_t pf_callback, void *p_data )
434 vlc_mutex_lock( &p_this->var_lock );
436 i_var = Lookup( p_this->p_vars, p_this->i_vars, psz_name );
439 vlc_mutex_unlock( &p_this->var_lock );
443 p_var = &p_this->p_vars[i_var];
445 for( i_entry = p_var->i_entries ; i_entry-- ; )
447 if( p_var->p_entries[i_entry].pf_callback == pf_callback
448 || p_var->p_entries[i_entry].p_data == p_data )
456 vlc_mutex_unlock( &p_this->var_lock );
462 memmove( p_var->p_entries + i_entry,
463 p_var->p_entries + i_entry + 1,
464 sizeof( callback_entry_t ) * ( p_var->i_entries - i_entry ) );
466 if( p_var->i_entries )
468 p_var->p_entries = realloc( p_var->p_entries,
469 sizeof( callback_entry_t ) * ( p_var->i_entries ) );
473 free( p_var->p_entries );
474 p_var->p_entries = NULL;
477 vlc_mutex_unlock( &p_this->var_lock );
482 /* Following functions are local */
484 /*****************************************************************************
485 * HashString: our cool hash function
486 *****************************************************************************
487 * This function is not intended to be crypto-secure, we only want it to be
488 * fast and not suck too much. This one is pretty fast and did 0 collisions
489 * in wenglish's dictionary.
490 *****************************************************************************/
491 static u32 HashString( const char *psz_string )
497 i_hash += *psz_string++;
498 i_hash += i_hash << 10;
499 i_hash ^= i_hash >> 8;
505 /*****************************************************************************
506 * Insert: find an empty slot to insert a new variable
507 *****************************************************************************
508 * We use a recursive inner function indexed on the hash. This function does
509 * nothing in the rare cases where a collision may occur, see Lookup()
510 * to see how we handle them.
511 * XXX: does this really need to be written recursively?
512 *****************************************************************************/
513 static int Insert( variable_t *p_vars, int i_count, const char *psz_name )
520 return InsertInner( p_vars, i_count, HashString( psz_name ) );
523 static int InsertInner( variable_t *p_vars, int i_count, u32 i_hash )
527 if( i_hash <= p_vars[0].i_hash )
532 if( i_hash >= p_vars[i_count - 1].i_hash )
537 i_middle = i_count / 2;
539 /* We know that 0 < i_middle */
540 if( i_hash < p_vars[i_middle].i_hash )
542 return InsertInner( p_vars, i_middle, i_hash );
545 /* We know that i_middle + 1 < i_count */
546 if( i_hash > p_vars[i_middle + 1].i_hash )
548 return i_middle + 1 + InsertInner( p_vars + i_middle + 1,
549 i_count - i_middle - 1,
556 /*****************************************************************************
557 * Lookup: find an existing variable given its name
558 *****************************************************************************
559 * We use a recursive inner function indexed on the hash. Care is taken of
560 * possible hash collisions.
561 * XXX: does this really need to be written recursively?
562 *****************************************************************************/
563 static int Lookup( variable_t *p_vars, int i_count, const char *psz_name )
573 i_hash = HashString( psz_name );
575 i_pos = LookupInner( p_vars, i_count, i_hash );
578 if( i_hash != p_vars[i_pos].i_hash )
583 /* Hash found, entry found */
584 if( !strcmp( psz_name, p_vars[i_pos].psz_name ) )
589 /* Hash collision! This should be very rare, but we cannot guarantee
590 * it will never happen. Just do an exhaustive search amongst all
591 * entries with the same hash. */
592 for( i = i_pos - 1 ; i > 0 && i_hash == p_vars[i].i_hash ; i-- )
594 if( !strcmp( psz_name, p_vars[i].psz_name ) )
600 for( i = i_pos + 1 ; i < i_count && i_hash == p_vars[i].i_hash ; i++ )
602 if( !strcmp( psz_name, p_vars[i].psz_name ) )
608 /* Hash found, but entry not found */
612 static int LookupInner( variable_t *p_vars, int i_count, u32 i_hash )
616 if( i_hash <= p_vars[0].i_hash )
621 if( i_hash >= p_vars[i_count-1].i_hash )
626 i_middle = i_count / 2;
628 /* We know that 0 < i_middle */
629 if( i_hash < p_vars[i_middle].i_hash )
631 return LookupInner( p_vars, i_middle, i_hash );
634 /* We know that i_middle + 1 < i_count */
635 if( i_hash > p_vars[i_middle].i_hash )
637 return i_middle + LookupInner( p_vars + i_middle,