1 /*****************************************************************************
2 * variables.c: routines for object variables handling
3 *****************************************************************************
4 * Copyright (C) 2002 VideoLAN
5 * $Id: variables.c,v 1.9 2002/10/28 13:25:56 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 int GetUnused ( vlc_object_t *, const char * );
46 static u32 HashString ( const char * );
47 static int Insert ( variable_t *, int, const char * );
48 static int InsertInner ( variable_t *, int, u32 );
49 static int Lookup ( variable_t *, int, const char * );
50 static int LookupInner ( variable_t *, int, u32 );
52 static void CheckValue ( variable_t *, vlc_value_t * );
54 /*****************************************************************************
55 * var_Create: initialize a vlc variable
56 *****************************************************************************
57 * We hash the given string and insert it into the sorted list. The insertion
58 * may require slow memory copies, but think about what we gain in the log(n)
59 * lookup phase when setting/getting the variable value!
60 *****************************************************************************/
61 int __var_Create( vlc_object_t *p_this, const char *psz_name, int i_type )
66 vlc_mutex_lock( &p_this->var_lock );
68 /* FIXME: if the variable already exists, we don't duplicate it. But we
69 * duplicate the lookups. It's not that serious, but if anyone finds some
70 * time to rework Insert() so that only one lookup has to be done, feel
72 i_new = Lookup( p_this->p_vars, p_this->i_vars, psz_name );
76 /* If the types differ, variable creation failed. */
77 if( i_type != p_this->p_vars[i_new].i_type )
79 vlc_mutex_unlock( &p_this->var_lock );
83 p_this->p_vars[i_new].i_usage++;
84 vlc_mutex_unlock( &p_this->var_lock );
88 i_new = Insert( p_this->p_vars, p_this->i_vars, psz_name );
90 if( (p_this->i_vars & 15) == 15 )
92 p_this->p_vars = realloc( p_this->p_vars,
93 (p_this->i_vars+17) * sizeof(variable_t) );
96 memmove( p_this->p_vars + i_new + 1,
97 p_this->p_vars + i_new,
98 (p_this->i_vars - i_new) * sizeof(variable_t) );
102 p_var = &p_this->p_vars[i_new];
104 p_var->i_hash = HashString( psz_name );
105 p_var->psz_name = strdup( psz_name );
107 p_var->i_type = i_type;
108 memset( &p_var->val, 0, sizeof(vlc_value_t) );
112 p_var->b_min = VLC_FALSE;
113 p_var->b_max = VLC_FALSE;
114 p_var->b_step = VLC_FALSE;
115 p_var->b_select = VLC_FALSE;
116 p_var->p_choice = NULL;
117 p_var->b_incallback = VLC_FALSE;
119 p_var->i_entries = 0;
120 p_var->p_entries = NULL;
122 /* Always initialize the variable */
126 p_var->val.b_bool = VLC_FALSE;
128 case VLC_VAR_INTEGER:
129 p_var->val.i_int = 0;
134 p_var->val.psz_string = strdup( "" );
137 p_var->val.f_float = 0.0;
142 case VLC_VAR_ADDRESS:
143 case VLC_VAR_COMMAND:
144 p_var->val.p_address = NULL;
147 p_var->val.p_address = malloc( sizeof(vlc_mutex_t) );
148 vlc_mutex_init( p_this, (vlc_mutex_t*)p_var->val.p_address );
152 vlc_mutex_unlock( &p_this->var_lock );
157 /*****************************************************************************
158 * var_Destroy: destroy a vlc variable
159 *****************************************************************************
160 * Look for the variable and destroy it if it is found. As in var_Create we
161 * do a call to memmove() but we have performance counterparts elsewhere.
162 *****************************************************************************/
163 int __var_Destroy( vlc_object_t *p_this, const char *psz_name )
168 vlc_mutex_lock( &p_this->var_lock );
170 i_var = GetUnused( p_this, psz_name );
173 vlc_mutex_unlock( &p_this->var_lock );
177 p_var = &p_this->p_vars[i_var];
179 if( p_var->i_usage > 1 )
182 vlc_mutex_unlock( &p_this->var_lock );
186 /* Free value if needed */
187 switch( p_var->i_type )
192 free( p_var->val.psz_string );
196 vlc_mutex_destroy( (vlc_mutex_t*)p_var->val.p_address );
197 free( p_var->val.p_address );
201 /* Free callbacks if needed */
202 if( p_var->p_entries )
204 free( p_var->p_entries );
207 free( p_var->psz_name );
209 memmove( p_this->p_vars + i_var,
210 p_this->p_vars + i_var + 1,
211 (p_this->i_vars - i_var - 1) * sizeof(variable_t) );
213 if( (p_this->i_vars & 15) == 0 )
215 p_this->p_vars = realloc( p_this->p_vars,
216 (p_this->i_vars) * sizeof( variable_t ) );
221 vlc_mutex_unlock( &p_this->var_lock );
226 /*****************************************************************************
227 * var_Change: perform an action on a variable
228 *****************************************************************************
230 *****************************************************************************/
231 int __var_Change( vlc_object_t *p_this, const char *psz_name,
232 int i_action, vlc_value_t *p_val )
237 vlc_mutex_lock( &p_this->var_lock );
239 i_var = Lookup( p_this->p_vars, p_this->i_vars, psz_name );
243 vlc_mutex_unlock( &p_this->var_lock );
247 p_var = &p_this->p_vars[i_var];
252 p_var->b_min = VLC_TRUE;
254 CheckValue( p_var, &p_var->val );
257 p_var->b_max = VLC_TRUE;
259 CheckValue( p_var, &p_var->val );
261 case VLC_VAR_SETSTEP:
262 p_var->b_step = VLC_TRUE;
263 p_var->step = *p_val;
264 CheckValue( p_var, &p_var->val );
267 case VLC_VAR_SETCHOICE:
268 p_var->b_select = VLC_TRUE;
275 vlc_mutex_unlock( &p_this->var_lock );
280 /*****************************************************************************
281 * var_Type: request a variable's type, 0 if not found
282 *****************************************************************************
284 *****************************************************************************/
285 int __var_Type( vlc_object_t *p_this, const char *psz_name )
289 vlc_mutex_lock( &p_this->var_lock );
291 i_var = Lookup( p_this->p_vars, p_this->i_vars, psz_name );
295 vlc_mutex_unlock( &p_this->var_lock );
299 i_type = p_this->p_vars[i_var].i_type;
301 vlc_mutex_unlock( &p_this->var_lock );
306 /*****************************************************************************
307 * var_Set: set a variable's value
308 *****************************************************************************
310 *****************************************************************************/
311 int __var_Set( vlc_object_t *p_this, const char *psz_name, vlc_value_t val )
317 vlc_mutex_lock( &p_this->var_lock );
319 i_var = GetUnused( p_this, psz_name );
322 vlc_mutex_unlock( &p_this->var_lock );
326 p_var = &p_this->p_vars[i_var];
328 /* Duplicate data if needed */
329 switch( p_var->i_type )
334 val.psz_string = strdup( val.psz_string );
338 /* Backup needed stuff */
341 /* Check boundaries */
342 CheckValue( p_var, &val );
344 /* Deal with callbacks. Tell we're in a callback, release the lock,
345 * call stored functions, retake the lock. */
346 if( p_var->i_entries )
349 int i_entries = p_var->i_entries;
350 callback_entry_t *p_entries = p_var->p_entries;
352 p_var->b_incallback = VLC_TRUE;
353 vlc_mutex_unlock( &p_this->var_lock );
356 for( ; i_entries-- ; )
358 p_entries[i_entries].pf_callback( p_this, psz_name, oldval, val,
359 p_entries[i_entries].p_data );
362 vlc_mutex_lock( &p_this->var_lock );
364 i_var = Lookup( p_this->p_vars, p_this->i_vars, psz_name );
367 msg_Err( p_this, "variable %s has disappeared" );
368 vlc_mutex_unlock( &p_this->var_lock );
372 p_var = &p_this->p_vars[i_var];
373 p_var->b_incallback = VLC_FALSE;
376 /* Set the variable */
379 /* Free data if needed */
380 switch( p_var->i_type )
385 free( oldval.psz_string );
389 vlc_mutex_unlock( &p_this->var_lock );
394 /*****************************************************************************
395 * var_Get: get a variable's value
396 *****************************************************************************
398 *****************************************************************************/
399 int __var_Get( vlc_object_t *p_this, const char *psz_name, vlc_value_t *p_val )
404 vlc_mutex_lock( &p_this->var_lock );
406 i_var = Lookup( p_this->p_vars, p_this->i_vars, psz_name );
410 vlc_mutex_unlock( &p_this->var_lock );
414 p_var = &p_this->p_vars[i_var];
416 /* Some variables trigger special behaviour. */
417 switch( p_var->i_type )
419 case VLC_VAR_COMMAND:
420 if( p_var->val.p_address )
422 /* We need to save data before releasing the lock */
424 int (*pf_command) (vlc_object_t *, char *, char *) =
425 p_var->val.p_address;
426 char *psz_cmd = strdup( p_var->psz_name );
427 char *psz_arg = strdup( p_val->psz_string );
429 vlc_mutex_unlock( &p_this->var_lock );
431 i_ret = pf_command( p_this, psz_cmd, psz_arg );
440 /* Really set the variable */
443 /* Duplicate value if needed */
444 switch( p_var->i_type )
449 if( p_val->psz_string )
451 p_val->psz_string = strdup( p_val->psz_string );
456 vlc_mutex_unlock( &p_this->var_lock );
461 /*****************************************************************************
462 * var_AddCallback: register a callback in a variable
463 *****************************************************************************
464 * We store a function pointer pf_callback that will be called upon variable
465 * modification. p_data is a generic pointer that will be passed as additional
466 * argument to the callback function.
467 *****************************************************************************/
468 int __var_AddCallback( vlc_object_t *p_this, const char *psz_name,
469 vlc_callback_t pf_callback, void *p_data )
474 vlc_mutex_lock( &p_this->var_lock );
476 i_var = GetUnused( p_this, psz_name );
479 vlc_mutex_unlock( &p_this->var_lock );
483 p_var = &p_this->p_vars[i_var];
484 i_entry = p_var->i_entries++;
488 p_var->p_entries = realloc( p_var->p_entries,
489 sizeof( callback_entry_t ) * p_var->i_entries );
493 p_var->p_entries = malloc( sizeof( callback_entry_t ) );
496 p_var->p_entries[ i_entry ].pf_callback = pf_callback;
497 p_var->p_entries[ i_entry ].p_data = p_data;
499 vlc_mutex_unlock( &p_this->var_lock );
504 /*****************************************************************************
505 * var_DelCallback: remove a callback from a variable
506 *****************************************************************************
507 * pf_callback and p_data have to be given again, because different objects
508 * might have registered the same callback function.
509 *****************************************************************************/
510 int __var_DelCallback( vlc_object_t *p_this, const char *psz_name,
511 vlc_callback_t pf_callback, void *p_data )
516 vlc_mutex_lock( &p_this->var_lock );
518 i_var = GetUnused( p_this, psz_name );
521 vlc_mutex_unlock( &p_this->var_lock );
525 p_var = &p_this->p_vars[i_var];
527 for( i_entry = p_var->i_entries ; i_entry-- ; )
529 if( p_var->p_entries[i_entry].pf_callback == pf_callback
530 || p_var->p_entries[i_entry].p_data == p_data )
538 vlc_mutex_unlock( &p_this->var_lock );
544 memmove( p_var->p_entries + i_entry,
545 p_var->p_entries + i_entry + 1,
546 sizeof( callback_entry_t ) * ( p_var->i_entries - i_entry ) );
548 if( p_var->i_entries )
550 p_var->p_entries = realloc( p_var->p_entries,
551 sizeof( callback_entry_t ) * ( p_var->i_entries ) );
555 free( p_var->p_entries );
556 p_var->p_entries = NULL;
559 vlc_mutex_unlock( &p_this->var_lock );
564 /* Following functions are local */
566 /*****************************************************************************
567 * GetUnused: find an unused variable from its name
568 *****************************************************************************
569 * We do i_tries tries before giving up, just in case the variable is being
570 * modified and called from a callback.
571 *****************************************************************************/
572 static int GetUnused( vlc_object_t *p_this, const char *psz_name )
574 int i_var, i_tries = 0;
578 i_var = Lookup( p_this->p_vars, p_this->i_vars, psz_name );
584 if( ! p_this->p_vars[i_var].b_incallback )
589 if( i_tries++ > 100 )
591 msg_Err( p_this, "caught in a callback deadlock?" );
595 vlc_mutex_unlock( &p_this->var_lock );
596 msleep( THREAD_SLEEP );
597 vlc_mutex_lock( &p_this->var_lock );
601 /*****************************************************************************
602 * HashString: our cool hash function
603 *****************************************************************************
604 * This function is not intended to be crypto-secure, we only want it to be
605 * fast and not suck too much. This one is pretty fast and did 0 collisions
606 * in wenglish's dictionary.
607 *****************************************************************************/
608 static u32 HashString( const char *psz_string )
614 i_hash += *psz_string++;
615 i_hash += i_hash << 10;
616 i_hash ^= i_hash >> 8;
622 /*****************************************************************************
623 * Insert: find an empty slot to insert a new variable
624 *****************************************************************************
625 * We use a recursive inner function indexed on the hash. This function does
626 * nothing in the rare cases where a collision may occur, see Lookup()
627 * to see how we handle them.
628 * XXX: does this really need to be written recursively?
629 *****************************************************************************/
630 static int Insert( variable_t *p_vars, int i_count, const char *psz_name )
637 return InsertInner( p_vars, i_count, HashString( psz_name ) );
640 static int InsertInner( variable_t *p_vars, int i_count, u32 i_hash )
644 if( i_hash <= p_vars[0].i_hash )
649 if( i_hash >= p_vars[i_count - 1].i_hash )
654 i_middle = i_count / 2;
656 /* We know that 0 < i_middle */
657 if( i_hash < p_vars[i_middle].i_hash )
659 return InsertInner( p_vars, i_middle, i_hash );
662 /* We know that i_middle + 1 < i_count */
663 if( i_hash > p_vars[i_middle + 1].i_hash )
665 return i_middle + 1 + InsertInner( p_vars + i_middle + 1,
666 i_count - i_middle - 1,
673 /*****************************************************************************
674 * Lookup: find an existing variable given its name
675 *****************************************************************************
676 * We use a recursive inner function indexed on the hash. Care is taken of
677 * possible hash collisions.
678 * XXX: does this really need to be written recursively?
679 *****************************************************************************/
680 static int Lookup( variable_t *p_vars, int i_count, const char *psz_name )
690 i_hash = HashString( psz_name );
692 i_pos = LookupInner( p_vars, i_count, i_hash );
695 if( i_hash != p_vars[i_pos].i_hash )
700 /* Hash found, entry found */
701 if( !strcmp( psz_name, p_vars[i_pos].psz_name ) )
706 /* Hash collision! This should be very rare, but we cannot guarantee
707 * it will never happen. Just do an exhaustive search amongst all
708 * entries with the same hash. */
709 for( i = i_pos - 1 ; i > 0 && i_hash == p_vars[i].i_hash ; i-- )
711 if( !strcmp( psz_name, p_vars[i].psz_name ) )
717 for( i = i_pos + 1 ; i < i_count && i_hash == p_vars[i].i_hash ; i++ )
719 if( !strcmp( psz_name, p_vars[i].psz_name ) )
725 /* Hash found, but entry not found */
729 static int LookupInner( variable_t *p_vars, int i_count, u32 i_hash )
733 if( i_hash <= p_vars[0].i_hash )
738 if( i_hash >= p_vars[i_count-1].i_hash )
743 i_middle = i_count / 2;
745 /* We know that 0 < i_middle */
746 if( i_hash < p_vars[i_middle].i_hash )
748 return LookupInner( p_vars, i_middle, i_hash );
751 /* We know that i_middle + 1 < i_count */
752 if( i_hash > p_vars[i_middle].i_hash )
754 return i_middle + LookupInner( p_vars + i_middle,
762 /*****************************************************************************
763 * CheckValue: check that a value is valid
764 *****************************************************************************
765 * This function checks p_val's value against p_var's limitations such as
766 * minimal and maximal value, step, in-list position, and changes p_val if
768 *****************************************************************************/
769 static void CheckValue ( variable_t *p_var, vlc_value_t *p_val )
771 switch( p_var->i_type )
773 case VLC_VAR_INTEGER:
774 if( p_var->b_step && p_var->step.i_int
775 && (p_val->i_int % p_var->step.i_int) )
777 p_val->i_int = (p_val->i_int + (p_var->step.i_int / 2))
778 / p_var->step.i_int * p_var->step.i_int;
780 if( p_var->b_min && p_val->i_int < p_var->min.i_int )
782 p_val->i_int = p_var->min.i_int;
784 if( p_var->b_max && p_val->i_int > p_var->max.i_int )
786 p_val->i_int = p_var->max.i_int;
790 if( p_var->b_step && p_var->step.f_float )
792 float f_round = p_var->step.f_float * (float)(int)( 0.5 +
793 p_val->f_float / p_var->step.f_float );
794 if( p_val->f_float != f_round )
796 p_val->f_float = f_round;
799 if( p_var->b_min && p_val->f_float < p_var->min.f_float )
801 p_val->f_float = p_var->min.f_float;
803 if( p_var->b_max && p_val->f_float > p_var->max.f_float )
805 p_val->f_float = p_var->max.f_float;