1 /*****************************************************************************
2 * variables.c: routines for object variables handling
3 *****************************************************************************
4 * Copyright (C) 2002 VideoLAN
5 * $Id: variables.c,v 1.29 2003/07/23 23:05:25 gbazin 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 /*****************************************************************************
43 * Local comparison functions, returns 0 if v == w, < 0 if v < w, > 0 if v > w
44 *****************************************************************************/
45 static int CmpBool( vlc_value_t v
, vlc_value_t w
) { return v
.b_bool
? w
.b_bool
? 0 : 1 : w
.b_bool
? -1 : 0; }
46 static int CmpInt( vlc_value_t v
, vlc_value_t w
) { return v
.i_int
== w
.i_int
? 0 : v
.i_int
> w
.i_int
? 1 : -1; }
47 static int CmpTime( vlc_value_t v
, vlc_value_t w
)
49 mtime_t v_time
,w_time
;
50 v_time
= ( (mtime_t
)v
.time
.i_high
<< 32 ) + v
.time
.i_low
;
51 w_time
= ( (mtime_t
)w
.time
.i_high
<< 32 ) + w
.time
.i_low
;
52 return v_time
== w_time
? 0 : v_time
> w_time
? 1 : -1;
54 static int CmpString( vlc_value_t v
, vlc_value_t w
) { return strcmp( v
.psz_string
, w
.psz_string
); }
55 static int CmpFloat( vlc_value_t v
, vlc_value_t w
) { return v
.f_float
== w
.f_float
? 0 : v
.f_float
> w
.f_float
? 1 : -1; }
56 static int CmpAddress( vlc_value_t v
, vlc_value_t w
) { return v
.p_address
== w
.p_address
? 0 : v
.p_address
> w
.p_address
? 1 : -1; }
58 /*****************************************************************************
59 * Local duplication functions, and local deallocation functions
60 *****************************************************************************/
61 static void DupDummy( vlc_value_t
*p_val
) { (void)p_val
; /* unused */ }
62 static void DupString( vlc_value_t
*p_val
) { p_val
->psz_string
= strdup( p_val
->psz_string
); }
64 static void DupList( vlc_value_t
*p_val
)
67 vlc_list_t
*p_list
= malloc( sizeof(vlc_list_t
) );
69 if( p_val
->p_list
->i_count
)
71 p_list
->i_count
= p_val
->p_list
->i_count
;
72 p_list
->p_values
= malloc( p_list
->i_count
* sizeof(vlc_value_t
) );
73 p_list
->pi_types
= malloc( p_list
->i_count
* sizeof(int) );
76 for( i
= 0; i
< p_list
->i_count
; i
++ )
78 p_list
->p_values
[i
] = p_val
->p_list
->p_values
[i
];
79 switch( p_val
->p_list
->pi_types
[i
] & VLC_VAR_TYPE
)
83 DupString( &p_list
->p_values
[i
] );
90 p_val
->p_list
= p_list
;
93 static void FreeDummy( vlc_value_t
*p_val
) { (void)p_val
; /* unused */ }
94 static void FreeString( vlc_value_t
*p_val
) { free( p_val
->psz_string
); }
95 static void FreeMutex( vlc_value_t
*p_val
) { vlc_mutex_destroy( (vlc_mutex_t
*)p_val
->p_address
); free( p_val
->p_address
); }
97 static void FreeList( vlc_value_t
*p_val
)
100 for( i
= 0; i
< p_val
->p_list
->i_count
; i
++ )
102 switch( p_val
->p_list
->pi_types
[i
] & VLC_VAR_TYPE
)
105 FreeString( &p_val
->p_list
->p_values
[i
] );
108 FreeMutex( &p_val
->p_list
->p_values
[i
] );
115 if( p_val
->p_list
->i_count
)
117 free( p_val
->p_list
->p_values
);
118 free( p_val
->p_list
->pi_types
);
120 free( p_val
->p_list
);
123 /*****************************************************************************
125 *****************************************************************************/
126 static int GetUnused ( vlc_object_t
*, const char * );
127 static uint32_t HashString ( const char * );
128 static int Insert ( variable_t
*, int, const char * );
129 static int InsertInner ( variable_t
*, int, uint32_t );
130 static int Lookup ( variable_t
*, int, const char * );
131 static int LookupInner ( variable_t
*, int, uint32_t );
133 static void CheckValue ( variable_t
*, vlc_value_t
* );
135 static int InheritValue( vlc_object_t
*, const char *, vlc_value_t
*,
138 /*****************************************************************************
139 * var_Create: initialize a vlc variable
140 *****************************************************************************
141 * We hash the given string and insert it into the sorted list. The insertion
142 * may require slow memory copies, but think about what we gain in the log(n)
143 * lookup phase when setting/getting the variable value!
144 *****************************************************************************/
145 int __var_Create( vlc_object_t
*p_this
, const char *psz_name
, int i_type
)
149 static vlc_list_t dummy_null_list
= {0, NULL
, NULL
};
151 vlc_mutex_lock( &p_this
->var_lock
);
153 /* FIXME: if the variable already exists, we don't duplicate it. But we
154 * duplicate the lookups. It's not that serious, but if anyone finds some
155 * time to rework Insert() so that only one lookup has to be done, feel
157 i_new
= Lookup( p_this
->p_vars
, p_this
->i_vars
, psz_name
);
161 /* If the types differ, variable creation failed. */
162 if( (i_type
& ~VLC_VAR_DOINHERIT
) != p_this
->p_vars
[i_new
].i_type
)
164 vlc_mutex_unlock( &p_this
->var_lock
);
168 p_this
->p_vars
[i_new
].i_usage
++;
169 vlc_mutex_unlock( &p_this
->var_lock
);
173 i_new
= Insert( p_this
->p_vars
, p_this
->i_vars
, psz_name
);
175 if( (p_this
->i_vars
& 15) == 15 )
177 p_this
->p_vars
= realloc( p_this
->p_vars
,
178 (p_this
->i_vars
+17) * sizeof(variable_t
) );
181 memmove( p_this
->p_vars
+ i_new
+ 1,
182 p_this
->p_vars
+ i_new
,
183 (p_this
->i_vars
- i_new
) * sizeof(variable_t
) );
187 p_var
= &p_this
->p_vars
[i_new
];
189 p_var
->i_hash
= HashString( psz_name
);
190 p_var
->psz_name
= strdup( psz_name
);
191 p_var
->psz_text
= NULL
;
193 p_var
->i_type
= i_type
& ~VLC_VAR_DOINHERIT
;
194 memset( &p_var
->val
, 0, sizeof(vlc_value_t
) );
196 p_var
->pf_dup
= DupDummy
;
197 p_var
->pf_free
= FreeDummy
;
201 p_var
->i_default
= -1;
202 p_var
->choices
.i_count
= 0;
203 p_var
->choices
.p_values
= NULL
;
204 p_var
->choices_text
.i_count
= 0;
205 p_var
->choices_text
.p_values
= NULL
;
207 p_var
->b_incallback
= VLC_FALSE
;
208 p_var
->i_entries
= 0;
209 p_var
->p_entries
= NULL
;
211 /* Always initialize the variable, even if it is a list variable; this
212 * will lead to errors if the variable is not initialized, but it will
213 * not cause crashes in the variable handling. */
214 switch( i_type
& VLC_VAR_TYPE
)
217 p_var
->pf_cmp
= CmpBool
;
218 p_var
->val
.b_bool
= VLC_FALSE
;
220 case VLC_VAR_INTEGER
:
221 p_var
->pf_cmp
= CmpInt
;
222 p_var
->val
.i_int
= 0;
227 case VLC_VAR_DIRECTORY
:
228 case VLC_VAR_VARIABLE
:
229 p_var
->pf_cmp
= CmpString
;
230 p_var
->pf_dup
= DupString
;
231 p_var
->pf_free
= FreeString
;
232 p_var
->val
.psz_string
= "";
235 p_var
->pf_cmp
= CmpFloat
;
236 p_var
->val
.f_float
= 0.0;
239 p_var
->pf_cmp
= CmpTime
;
240 p_var
->val
.time
.i_low
= 0;
241 p_var
->val
.time
.i_high
= 0;
243 case VLC_VAR_ADDRESS
:
244 p_var
->pf_cmp
= CmpAddress
;
245 p_var
->val
.p_address
= NULL
;
248 p_var
->pf_cmp
= CmpAddress
;
249 p_var
->pf_free
= FreeMutex
;
250 p_var
->val
.p_address
= malloc( sizeof(vlc_mutex_t
) );
251 vlc_mutex_init( p_this
, (vlc_mutex_t
*)p_var
->val
.p_address
);
254 p_var
->pf_cmp
= CmpAddress
;
255 p_var
->pf_dup
= DupList
;
256 p_var
->pf_free
= FreeList
;
257 p_var
->val
.p_list
= &dummy_null_list
;
261 /* Duplicate the default data we stored. */
262 p_var
->pf_dup( &p_var
->val
);
264 if( i_type
& VLC_VAR_DOINHERIT
)
268 if( InheritValue( p_this
, psz_name
, &val
, p_var
->i_type
)
271 /* Free data if needed */
272 p_var
->pf_free( &p_var
->val
);
273 /* Check boundaries and list */
274 CheckValue( p_var
, &val
);
275 /* Set the variable */
280 vlc_mutex_unlock( &p_this
->var_lock
);
285 /*****************************************************************************
286 * var_Destroy: destroy a vlc variable
287 *****************************************************************************
288 * Look for the variable and destroy it if it is found. As in var_Create we
289 * do a call to memmove() but we have performance counterparts elsewhere.
290 *****************************************************************************/
291 int __var_Destroy( vlc_object_t
*p_this
, const char *psz_name
)
296 vlc_mutex_lock( &p_this
->var_lock
);
298 i_var
= GetUnused( p_this
, psz_name
);
301 vlc_mutex_unlock( &p_this
->var_lock
);
305 p_var
= &p_this
->p_vars
[i_var
];
307 if( p_var
->i_usage
> 1 )
310 vlc_mutex_unlock( &p_this
->var_lock
);
314 /* Free value if needed */
315 p_var
->pf_free( &p_var
->val
);
317 /* Free choice list if needed */
318 if( p_var
->choices
.i_count
)
320 for( i
= 0 ; i
< p_var
->choices
.i_count
; i
++ )
322 p_var
->pf_free( &p_var
->choices
.p_values
[i
] );
323 if( p_var
->choices_text
.p_values
[i
].psz_string
)
324 free( p_var
->choices_text
.p_values
[i
].psz_string
);
326 free( p_var
->choices
.p_values
);
327 free( p_var
->choices_text
.p_values
);
330 /* Free callbacks if needed */
331 if( p_var
->p_entries
)
333 free( p_var
->p_entries
);
336 free( p_var
->psz_name
);
337 if( p_var
->psz_text
) free( p_var
->psz_text
);
339 memmove( p_this
->p_vars
+ i_var
,
340 p_this
->p_vars
+ i_var
+ 1,
341 (p_this
->i_vars
- i_var
- 1) * sizeof(variable_t
) );
343 if( (p_this
->i_vars
& 15) == 0 )
345 p_this
->p_vars
= realloc( p_this
->p_vars
,
346 (p_this
->i_vars
) * sizeof( variable_t
) );
351 vlc_mutex_unlock( &p_this
->var_lock
);
356 /*****************************************************************************
357 * var_Change: perform an action on a variable
358 *****************************************************************************
360 *****************************************************************************/
361 int __var_Change( vlc_object_t
*p_this
, const char *psz_name
,
362 int i_action
, vlc_value_t
*p_val
, vlc_value_t
*p_val2
)
368 vlc_mutex_lock( &p_this
->var_lock
);
370 i_var
= Lookup( p_this
->p_vars
, p_this
->i_vars
, psz_name
);
374 vlc_mutex_unlock( &p_this
->var_lock
);
378 p_var
= &p_this
->p_vars
[i_var
];
383 if( p_var
->i_type
& VLC_VAR_HASMIN
)
385 p_var
->pf_free( &p_var
->min
);
387 p_var
->i_type
|= VLC_VAR_HASMIN
;
389 p_var
->pf_dup( &p_var
->min
);
390 CheckValue( p_var
, &p_var
->val
);
393 if( p_var
->i_type
& VLC_VAR_HASMAX
)
395 p_var
->pf_free( &p_var
->max
);
397 p_var
->i_type
|= VLC_VAR_HASMAX
;
399 p_var
->pf_dup( &p_var
->max
);
400 CheckValue( p_var
, &p_var
->val
);
402 case VLC_VAR_SETSTEP
:
403 if( p_var
->i_type
& VLC_VAR_HASSTEP
)
405 p_var
->pf_free( &p_var
->step
);
407 p_var
->i_type
|= VLC_VAR_HASSTEP
;
408 p_var
->step
= *p_val
;
409 p_var
->pf_dup( &p_var
->step
);
410 CheckValue( p_var
, &p_var
->val
);
412 case VLC_VAR_ADDCHOICE
:
413 /* FIXME: the list is sorted, dude. Use something cleverer. */
414 for( i
= p_var
->choices
.i_count
; i
-- ; )
416 if( p_var
->pf_cmp( p_var
->choices
.p_values
[i
], *p_val
) < 0 )
422 /* The new place is i+1 */
425 if( p_var
->i_default
>= i
)
430 INSERT_ELEM( p_var
->choices
.p_values
, p_var
->choices
.i_count
,
432 INSERT_ELEM( p_var
->choices_text
.p_values
,
433 p_var
->choices_text
.i_count
, i
, *p_val
);
434 p_var
->pf_dup( &p_var
->choices
.p_values
[i
] );
435 p_var
->choices_text
.p_values
[i
].psz_string
=
436 ( p_val2
&& p_val2
->psz_string
) ?
437 strdup( p_val2
->psz_string
) : NULL
;
439 CheckValue( p_var
, &p_var
->val
);
441 case VLC_VAR_DELCHOICE
:
442 /* FIXME: the list is sorted, dude. Use something cleverer. */
443 for( i
= 0 ; i
< p_var
->choices
.i_count
; i
++ )
445 if( p_var
->pf_cmp( p_var
->choices
.p_values
[i
], *p_val
) == 0 )
451 if( i
== p_var
->choices
.i_count
)
454 vlc_mutex_unlock( &p_this
->var_lock
);
458 if( p_var
->i_default
> i
)
462 else if( p_var
->i_default
== i
)
464 p_var
->i_default
= -1;
467 p_var
->pf_free( &p_var
->choices
.p_values
[i
] );
468 if( p_var
->choices_text
.p_values
[i
].psz_string
)
469 free( p_var
->choices_text
.p_values
[i
].psz_string
);
470 REMOVE_ELEM( p_var
->choices
.p_values
, p_var
->choices
.i_count
, i
);
471 REMOVE_ELEM( p_var
->choices_text
.p_values
,
472 p_var
->choices_text
.i_count
, i
);
474 CheckValue( p_var
, &p_var
->val
);
476 case VLC_VAR_CHOICESCOUNT
:
477 p_val
->i_int
= p_var
->choices
.i_count
;
479 case VLC_VAR_CLEARCHOICES
:
480 for( i
= 0 ; i
< p_var
->choices
.i_count
; i
++ )
482 p_var
->pf_free( &p_var
->choices
.p_values
[i
] );
484 if( p_var
->choices
.i_count
)
485 free( p_var
->choices
.p_values
);
487 p_var
->choices
.i_count
= 0;
488 p_var
->choices
.p_values
= NULL
;
489 p_var
->i_default
= -1;
491 case VLC_VAR_SETDEFAULT
:
492 /* FIXME: the list is sorted, dude. Use something cleverer. */
493 for( i
= 0 ; i
< p_var
->choices
.i_count
; i
++ )
495 if( p_var
->pf_cmp( p_var
->choices
.p_values
[i
], *p_val
) == 0 )
501 if( i
== p_var
->choices
.i_count
)
507 p_var
->i_default
= i
;
508 CheckValue( p_var
, &p_var
->val
);
510 case VLC_VAR_SETVALUE
:
511 /* Duplicate data if needed */
512 p_var
->pf_dup( p_val
);
513 /* Backup needed stuff */
515 /* Check boundaries and list */
516 CheckValue( p_var
, p_val
);
517 /* Set the variable */
519 /* Free data if needed */
520 p_var
->pf_free( &oldval
);
522 case VLC_VAR_GETCHOICES
:
523 case VLC_VAR_GETLIST
:
524 p_val
->p_list
= malloc( sizeof(vlc_list_t
) );
525 if( p_val2
) p_val2
->p_list
= malloc( sizeof(vlc_list_t
) );
526 if( p_var
->choices
.i_count
)
528 p_val
->p_list
->p_values
= malloc( p_var
->choices
.i_count
529 * sizeof(vlc_value_t
) );
530 p_val
->p_list
->pi_types
= malloc( p_var
->choices
.i_count
534 p_val2
->p_list
->p_values
=
535 malloc( p_var
->choices
.i_count
* sizeof(vlc_value_t
) );
536 p_val2
->p_list
->pi_types
=
537 malloc( p_var
->choices
.i_count
* sizeof(int) );
540 p_val
->p_list
->i_count
= p_var
->choices
.i_count
;
541 if( p_val2
) p_val2
->p_list
->i_count
= p_var
->choices
.i_count
;
542 for( i
= 0 ; i
< p_var
->choices
.i_count
; i
++ )
544 p_val
->p_list
->p_values
[i
] = p_var
->choices
.p_values
[i
];
545 p_val
->p_list
->pi_types
[i
] = p_var
->i_type
;
546 p_var
->pf_dup( &p_val
->p_list
->p_values
[i
] );
549 p_val2
->p_list
->p_values
[i
].psz_string
=
550 p_var
->choices_text
.p_values
[i
].psz_string
?
551 strdup(p_var
->choices_text
.p_values
[i
].psz_string
) : NULL
;
552 p_val2
->p_list
->pi_types
[i
] = VLC_VAR_STRING
;
556 case VLC_VAR_FREELIST
:
559 case VLC_VAR_SETTEXT
:
560 if( p_var
->psz_text
) free( p_var
->psz_text
);
561 if( p_val
&& p_val
->psz_string
)
562 p_var
->psz_text
= strdup( p_val
->psz_string
);
564 case VLC_VAR_GETTEXT
:
565 p_val
->psz_string
= NULL
;
566 if( p_var
->psz_text
)
568 p_val
->psz_string
= strdup( p_var
->psz_text
);
571 case VLC_VAR_INHERITVALUE
:
575 if( InheritValue( p_this
, psz_name
, &val
, p_var
->i_type
)
578 /* Duplicate already done */
580 /* Backup needed stuff */
582 /* Check boundaries and list */
583 CheckValue( p_var
, &val
);
584 /* Set the variable */
586 /* Free data if needed */
587 p_var
->pf_free( &oldval
);
593 p_var
->pf_dup( p_val
);
602 vlc_mutex_unlock( &p_this
->var_lock
);
607 /*****************************************************************************
608 * var_Type: request a variable's type
609 *****************************************************************************
610 * This function returns the variable type if it exists, or 0 if the
611 * variable could not be found.
612 *****************************************************************************/
613 int __var_Type( vlc_object_t
*p_this
, const char *psz_name
)
617 vlc_mutex_lock( &p_this
->var_lock
);
619 i_var
= Lookup( p_this
->p_vars
, p_this
->i_vars
, psz_name
);
623 vlc_mutex_unlock( &p_this
->var_lock
);
627 i_type
= p_this
->p_vars
[i_var
].i_type
;
629 vlc_mutex_unlock( &p_this
->var_lock
);
634 /*****************************************************************************
635 * var_Set: set a variable's value
636 *****************************************************************************
638 *****************************************************************************/
639 int __var_Set( vlc_object_t
*p_this
, const char *psz_name
, vlc_value_t val
)
645 vlc_mutex_lock( &p_this
->var_lock
);
647 i_var
= GetUnused( p_this
, psz_name
);
650 vlc_mutex_unlock( &p_this
->var_lock
);
654 p_var
= &p_this
->p_vars
[i_var
];
656 /* Duplicate data if needed */
657 p_var
->pf_dup( &val
);
659 /* Backup needed stuff */
662 /* Check boundaries and list */
663 CheckValue( p_var
, &val
);
665 /* Set the variable */
668 /* Deal with callbacks. Tell we're in a callback, release the lock,
669 * call stored functions, retake the lock. */
670 if( p_var
->i_entries
)
673 int i_entries
= p_var
->i_entries
;
674 callback_entry_t
*p_entries
= p_var
->p_entries
;
676 p_var
->b_incallback
= VLC_TRUE
;
677 vlc_mutex_unlock( &p_this
->var_lock
);
680 for( ; i_entries
-- ; )
682 p_entries
[i_entries
].pf_callback( p_this
, psz_name
, oldval
, val
,
683 p_entries
[i_entries
].p_data
);
686 vlc_mutex_lock( &p_this
->var_lock
);
688 i_var
= Lookup( p_this
->p_vars
, p_this
->i_vars
, psz_name
);
691 msg_Err( p_this
, "variable %s has disappeared", psz_name
);
692 vlc_mutex_unlock( &p_this
->var_lock
);
696 p_var
= &p_this
->p_vars
[i_var
];
697 p_var
->b_incallback
= VLC_FALSE
;
700 /* Free data if needed */
701 p_var
->pf_free( &oldval
);
703 vlc_mutex_unlock( &p_this
->var_lock
);
708 /*****************************************************************************
709 * var_Get: get a variable's value
710 *****************************************************************************
712 *****************************************************************************/
713 int __var_Get( vlc_object_t
*p_this
, const char *psz_name
, vlc_value_t
*p_val
)
718 vlc_mutex_lock( &p_this
->var_lock
);
720 i_var
= Lookup( p_this
->p_vars
, p_this
->i_vars
, psz_name
);
724 vlc_mutex_unlock( &p_this
->var_lock
);
728 p_var
= &p_this
->p_vars
[i_var
];
730 /* Really get the variable */
733 /* Duplicate value if needed */
734 p_var
->pf_dup( p_val
);
736 vlc_mutex_unlock( &p_this
->var_lock
);
741 /*****************************************************************************
742 * var_AddCallback: register a callback in a variable
743 *****************************************************************************
744 * We store a function pointer pf_callback that will be called upon variable
745 * modification. p_data is a generic pointer that will be passed as additional
746 * argument to the callback function.
747 *****************************************************************************/
748 int __var_AddCallback( vlc_object_t
*p_this
, const char *psz_name
,
749 vlc_callback_t pf_callback
, void *p_data
)
753 callback_entry_t entry
;
755 entry
.pf_callback
= pf_callback
;
756 entry
.p_data
= p_data
;
758 vlc_mutex_lock( &p_this
->var_lock
);
760 i_var
= GetUnused( p_this
, psz_name
);
763 vlc_mutex_unlock( &p_this
->var_lock
);
767 p_var
= &p_this
->p_vars
[i_var
];
769 INSERT_ELEM( p_var
->p_entries
,
774 vlc_mutex_unlock( &p_this
->var_lock
);
779 /*****************************************************************************
780 * var_DelCallback: remove a callback from a variable
781 *****************************************************************************
782 * pf_callback and p_data have to be given again, because different objects
783 * might have registered the same callback function.
784 *****************************************************************************/
785 int __var_DelCallback( vlc_object_t
*p_this
, const char *psz_name
,
786 vlc_callback_t pf_callback
, void *p_data
)
791 vlc_mutex_lock( &p_this
->var_lock
);
793 i_var
= GetUnused( p_this
, psz_name
);
796 vlc_mutex_unlock( &p_this
->var_lock
);
800 p_var
= &p_this
->p_vars
[i_var
];
802 for( i_entry
= p_var
->i_entries
; i_entry
-- ; )
804 if( p_var
->p_entries
[i_entry
].pf_callback
== pf_callback
805 && p_var
->p_entries
[i_entry
].p_data
== p_data
)
813 vlc_mutex_unlock( &p_this
->var_lock
);
817 REMOVE_ELEM( p_var
->p_entries
, p_var
->i_entries
, i_entry
);
819 vlc_mutex_unlock( &p_this
->var_lock
);
824 /* Following functions are local */
826 /*****************************************************************************
827 * GetUnused: find an unused variable from its name
828 *****************************************************************************
829 * We do i_tries tries before giving up, just in case the variable is being
830 * modified and called from a callback.
831 *****************************************************************************/
832 static int GetUnused( vlc_object_t
*p_this
, const char *psz_name
)
834 int i_var
, i_tries
= 0;
838 i_var
= Lookup( p_this
->p_vars
, p_this
->i_vars
, psz_name
);
844 if( ! p_this
->p_vars
[i_var
].b_incallback
)
849 if( i_tries
++ > 100 )
851 msg_Err( p_this
, "caught in a callback deadlock?" );
855 vlc_mutex_unlock( &p_this
->var_lock
);
856 msleep( THREAD_SLEEP
);
857 vlc_mutex_lock( &p_this
->var_lock
);
861 /*****************************************************************************
862 * HashString: our cool hash function
863 *****************************************************************************
864 * This function is not intended to be crypto-secure, we only want it to be
865 * fast and not suck too much. This one is pretty fast and did 0 collisions
866 * in wenglish's dictionary.
867 *****************************************************************************/
868 static uint32_t HashString( const char *psz_string
)
874 i_hash
+= *psz_string
++;
875 i_hash
+= i_hash
<< 10;
876 i_hash
^= i_hash
>> 8;
882 /*****************************************************************************
883 * Insert: find an empty slot to insert a new variable
884 *****************************************************************************
885 * We use a recursive inner function indexed on the hash. This function does
886 * nothing in the rare cases where a collision may occur, see Lookup()
887 * to see how we handle them.
888 * XXX: does this really need to be written recursively?
889 *****************************************************************************/
890 static int Insert( variable_t
*p_vars
, int i_count
, const char *psz_name
)
897 return InsertInner( p_vars
, i_count
, HashString( psz_name
) );
900 static int InsertInner( variable_t
*p_vars
, int i_count
, uint32_t i_hash
)
904 if( i_hash
<= p_vars
[0].i_hash
)
909 if( i_hash
>= p_vars
[i_count
- 1].i_hash
)
914 i_middle
= i_count
/ 2;
916 /* We know that 0 < i_middle */
917 if( i_hash
< p_vars
[i_middle
].i_hash
)
919 return InsertInner( p_vars
, i_middle
, i_hash
);
922 /* We know that i_middle + 1 < i_count */
923 if( i_hash
> p_vars
[i_middle
+ 1].i_hash
)
925 return i_middle
+ 1 + InsertInner( p_vars
+ i_middle
+ 1,
926 i_count
- i_middle
- 1,
933 /*****************************************************************************
934 * Lookup: find an existing variable given its name
935 *****************************************************************************
936 * We use a recursive inner function indexed on the hash. Care is taken of
937 * possible hash collisions.
938 * XXX: does this really need to be written recursively?
939 *****************************************************************************/
940 static int Lookup( variable_t
*p_vars
, int i_count
, const char *psz_name
)
950 i_hash
= HashString( psz_name
);
952 i_pos
= LookupInner( p_vars
, i_count
, i_hash
);
955 if( i_hash
!= p_vars
[i_pos
].i_hash
)
960 /* Hash found, entry found */
961 if( !strcmp( psz_name
, p_vars
[i_pos
].psz_name
) )
966 /* Hash collision! This should be very rare, but we cannot guarantee
967 * it will never happen. Just do an exhaustive search amongst all
968 * entries with the same hash. */
969 for( i
= i_pos
- 1 ; i
> 0 && i_hash
== p_vars
[i
].i_hash
; i
-- )
971 if( !strcmp( psz_name
, p_vars
[i
].psz_name
) )
977 for( i
= i_pos
+ 1 ; i
< i_count
&& i_hash
== p_vars
[i
].i_hash
; i
++ )
979 if( !strcmp( psz_name
, p_vars
[i
].psz_name
) )
985 /* Hash found, but entry not found */
989 static int LookupInner( variable_t
*p_vars
, int i_count
, uint32_t i_hash
)
993 if( i_hash
<= p_vars
[0].i_hash
)
998 if( i_hash
>= p_vars
[i_count
-1].i_hash
)
1003 i_middle
= i_count
/ 2;
1005 /* We know that 0 < i_middle */
1006 if( i_hash
< p_vars
[i_middle
].i_hash
)
1008 return LookupInner( p_vars
, i_middle
, i_hash
);
1011 /* We know that i_middle + 1 < i_count */
1012 if( i_hash
> p_vars
[i_middle
].i_hash
)
1014 return i_middle
+ LookupInner( p_vars
+ i_middle
,
1022 /*****************************************************************************
1023 * CheckValue: check that a value is valid wrt. a variable
1024 *****************************************************************************
1025 * This function checks p_val's value against p_var's limitations such as
1026 * minimal and maximal value, step, in-list position, and modifies p_val if
1028 *****************************************************************************/
1029 static void CheckValue ( variable_t
*p_var
, vlc_value_t
*p_val
)
1031 /* Check that our variable is in the list */
1032 if( p_var
->i_type
& VLC_VAR_HASCHOICE
&& p_var
->choices
.i_count
)
1036 /* FIXME: the list is sorted, dude. Use something cleverer. */
1037 for( i
= p_var
->choices
.i_count
; i
-- ; )
1039 if( p_var
->pf_cmp( *p_val
, p_var
->choices
.p_values
[i
] ) == 0 )
1045 /* If not found, change it to anything vaguely valid */
1048 /* Free the old variable, get the new one, dup it */
1049 p_var
->pf_free( p_val
);
1050 *p_val
= p_var
->choices
.p_values
[p_var
->i_default
>= 0
1051 ? p_var
->i_default
: 0 ];
1052 p_var
->pf_dup( p_val
);
1056 /* Check that our variable is within the bounds */
1057 switch( p_var
->i_type
& VLC_VAR_TYPE
)
1059 case VLC_VAR_INTEGER
:
1060 if( p_var
->i_type
& VLC_VAR_HASSTEP
&& p_var
->step
.i_int
1061 && (p_val
->i_int
% p_var
->step
.i_int
) )
1063 p_val
->i_int
= (p_val
->i_int
+ (p_var
->step
.i_int
/ 2))
1064 / p_var
->step
.i_int
* p_var
->step
.i_int
;
1066 if( p_var
->i_type
& VLC_VAR_HASMIN
1067 && p_val
->i_int
< p_var
->min
.i_int
)
1069 p_val
->i_int
= p_var
->min
.i_int
;
1071 if( p_var
->i_type
& VLC_VAR_HASMAX
1072 && p_val
->i_int
> p_var
->max
.i_int
)
1074 p_val
->i_int
= p_var
->max
.i_int
;
1078 if( p_var
->i_type
& VLC_VAR_HASSTEP
&& p_var
->step
.f_float
)
1080 float f_round
= p_var
->step
.f_float
* (float)(int)( 0.5 +
1081 p_val
->f_float
/ p_var
->step
.f_float
);
1082 if( p_val
->f_float
!= f_round
)
1084 p_val
->f_float
= f_round
;
1087 if( p_var
->i_type
& VLC_VAR_HASMIN
1088 && p_val
->f_float
< p_var
->min
.f_float
)
1090 p_val
->f_float
= p_var
->min
.f_float
;
1092 if( p_var
->i_type
& VLC_VAR_HASMAX
1093 && p_val
->f_float
> p_var
->max
.f_float
)
1095 p_val
->f_float
= p_var
->max
.f_float
;
1104 /*****************************************************************************
1105 * InheritValue: try to inherit the value of this variable from the same one
1106 * in our closest parent.
1107 *****************************************************************************/
1108 static int InheritValue( vlc_object_t
*p_this
, const char *psz_name
,
1109 vlc_value_t
*p_val
, int i_type
)
1114 /* No need to take the structure lock,
1115 * we are only looking for our parents */
1117 if( !p_this
->p_parent
)
1119 switch( i_type
& VLC_VAR_TYPE
)
1122 case VLC_VAR_DIRECTORY
:
1123 case VLC_VAR_STRING
:
1124 p_val
->psz_string
= config_GetPsz( p_this
, psz_name
);
1125 if( !p_val
->psz_string
) p_val
->psz_string
= strdup("");
1128 p_val
->f_float
= config_GetFloat( p_this
, psz_name
);
1130 case VLC_VAR_INTEGER
:
1131 p_val
->i_int
= config_GetInt( p_this
, psz_name
);
1134 p_val
->b_bool
= config_GetInt( p_this
, psz_name
);
1144 /* Look for the variable */
1145 vlc_mutex_lock( &p_this
->p_parent
->var_lock
);
1147 i_var
= Lookup( p_this
->p_parent
->p_vars
, p_this
->p_parent
->i_vars
,
1154 p_var
= &p_this
->p_parent
->p_vars
[i_var
];
1156 /* Really get the variable */
1157 *p_val
= p_var
->val
;
1159 /* Duplicate value if needed */
1160 p_var
->pf_dup( p_val
);
1162 vlc_mutex_unlock( &p_this
->p_parent
->var_lock
);
1166 vlc_mutex_unlock( &p_this
->p_parent
->var_lock
);
1168 /* We're still not there */
1170 return InheritValue( p_this
->p_parent
, psz_name
, p_val
, i_type
);