]> git.sesse.net Git - vlc/blob - src/misc/variables.c
* src/misc/variables.c, ALL: improvements to the object variables api.
[vlc] / src / misc / variables.c
1 /*****************************************************************************
2  * variables.c: routines for object variables handling
3  *****************************************************************************
4  * Copyright (C) 2002 VideoLAN
5  * $Id: variables.c,v 1.22 2003/05/04 22:42:18 gbazin Exp $
6  *
7  * Authors: Samuel Hocevar <sam@zoy.org>
8  *
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.
13  *
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.
18  *
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  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #include <vlc/vlc.h>
28
29 #ifdef HAVE_STDLIB_H
30 #   include <stdlib.h>                                          /* realloc() */
31 #endif
32
33 /*****************************************************************************
34  * Private types
35  *****************************************************************************/
36 struct callback_entry_t
37 {
38     vlc_callback_t pf_callback;
39     void *         p_data;
40 };
41
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 CmpString( vlc_value_t v, vlc_value_t w ) { return strcmp( v.psz_string, w.psz_string ); }
48 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; }
49 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; }
50
51 /*****************************************************************************
52  * Local duplication functions, and local deallocation functions
53  *****************************************************************************/
54 static void DupDummy( vlc_value_t *p_val ) { (void)p_val; /* unused */ }
55 static void DupString( vlc_value_t *p_val ) { p_val->psz_string = strdup( p_val->psz_string ); }
56
57 static void DupList( vlc_value_t *p_val )
58 {
59     int i;
60     vlc_list_t *p_list = malloc( sizeof(vlc_list_t) );
61
62     if( p_val->p_list->i_count )
63     {
64         p_list->i_count = p_val->p_list->i_count;
65         p_list->p_values = malloc( p_list->i_count * sizeof(vlc_value_t) );
66         p_list->pi_types = malloc( p_list->i_count * sizeof(int) );
67     }
68
69     for( i = 0; i < p_list->i_count; i++ )
70     {
71         p_list->p_values[i] = p_val->p_list->p_values[i];
72         switch( p_val->p_list->pi_types[i] & VLC_VAR_TYPE )
73         {
74         case VLC_VAR_STRING:
75             
76             DupString( &p_list->p_values[i] );
77             break;
78         default:
79             break;
80         }
81     }
82
83     p_val->p_list = p_list;
84 }
85
86 static void FreeDummy( vlc_value_t *p_val ) { (void)p_val; /* unused */ }
87 static void FreeString( vlc_value_t *p_val ) { free( p_val->psz_string ); }
88 static void FreeMutex( vlc_value_t *p_val ) { vlc_mutex_destroy( (vlc_mutex_t*)p_val->p_address ); free( p_val->p_address ); }
89
90 static void FreeList( vlc_value_t *p_val )
91 {
92     int i;
93     for( i = 0; i < p_val->p_list->i_count; i++ )
94     {
95         switch( p_val->p_list->pi_types[i] & VLC_VAR_TYPE )
96         {
97         case VLC_VAR_STRING:
98             FreeString( &p_val->p_list->p_values[i] );
99             break;
100         case VLC_VAR_MUTEX:
101             FreeMutex( &p_val->p_list->p_values[i] );
102             break;
103         default:
104             break;
105         }
106     }
107
108     if( p_val->p_list->i_count )
109     {
110         free( p_val->p_list->p_values );
111         free( p_val->p_list->pi_types );
112     }
113     free( p_val->p_list );
114 }
115
116 /*****************************************************************************
117  * Local prototypes
118  *****************************************************************************/
119 static int      GetUnused   ( vlc_object_t *, const char * );
120 static uint32_t HashString  ( const char * );
121 static int      Insert      ( variable_t *, int, const char * );
122 static int      InsertInner ( variable_t *, int, uint32_t );
123 static int      Lookup      ( variable_t *, int, const char * );
124 static int      LookupInner ( variable_t *, int, uint32_t );
125
126 static void     CheckValue  ( variable_t *, vlc_value_t * );
127
128 /*****************************************************************************
129  * var_Create: initialize a vlc variable
130  *****************************************************************************
131  * We hash the given string and insert it into the sorted list. The insertion
132  * may require slow memory copies, but think about what we gain in the log(n)
133  * lookup phase when setting/getting the variable value!
134  *****************************************************************************/
135 int __var_Create( vlc_object_t *p_this, const char *psz_name, int i_type )
136 {
137     int i_new;
138     variable_t *p_var;
139     static vlc_list_t dummy_null_list = {0, NULL, NULL};
140
141     vlc_mutex_lock( &p_this->var_lock );
142
143     /* FIXME: if the variable already exists, we don't duplicate it. But we
144      * duplicate the lookups. It's not that serious, but if anyone finds some
145      * time to rework Insert() so that only one lookup has to be done, feel
146      * free to do so. */
147     i_new = Lookup( p_this->p_vars, p_this->i_vars, psz_name );
148
149     if( i_new >= 0 )
150     {
151         /* If the types differ, variable creation failed. */
152         if( i_type != p_this->p_vars[i_new].i_type )
153         {
154             vlc_mutex_unlock( &p_this->var_lock );
155             return VLC_EBADVAR;
156         }
157
158         p_this->p_vars[i_new].i_usage++;
159         vlc_mutex_unlock( &p_this->var_lock );
160         return VLC_SUCCESS;
161     }
162
163     i_new = Insert( p_this->p_vars, p_this->i_vars, psz_name );
164
165     if( (p_this->i_vars & 15) == 15 )
166     {
167         p_this->p_vars = realloc( p_this->p_vars,
168                                   (p_this->i_vars+17) * sizeof(variable_t) );
169     }
170
171     memmove( p_this->p_vars + i_new + 1,
172              p_this->p_vars + i_new,
173              (p_this->i_vars - i_new) * sizeof(variable_t) );
174
175     p_this->i_vars++;
176
177     p_var = &p_this->p_vars[i_new];
178
179     p_var->i_hash = HashString( psz_name );
180     p_var->psz_name = strdup( psz_name );
181     p_var->psz_text = NULL;
182
183     p_var->i_type = i_type;
184     memset( &p_var->val, 0, sizeof(vlc_value_t) );
185
186     p_var->pf_dup = DupDummy;
187     p_var->pf_free = FreeDummy;
188
189     p_var->i_usage = 1;
190
191     p_var->i_default = -1;
192     p_var->choices.i_count = 0;
193     p_var->choices.p_values = NULL;
194     p_var->choices_text.i_count = 0;
195     p_var->choices_text.p_values = NULL;
196
197     p_var->b_incallback = VLC_FALSE;
198     p_var->i_entries = 0;
199     p_var->p_entries = NULL;
200
201     /* Always initialize the variable, even if it is a list variable; this
202      * will lead to errors if the variable is not initialized, but it will
203      * not cause crashes in the variable handling. */
204     switch( i_type & VLC_VAR_TYPE )
205     {
206         case VLC_VAR_BOOL:
207             p_var->pf_cmp = CmpBool;
208             p_var->val.b_bool = VLC_FALSE;
209             break;
210         case VLC_VAR_INTEGER:
211             p_var->pf_cmp = CmpInt;
212             p_var->val.i_int = 0;
213             break;
214         case VLC_VAR_STRING:
215         case VLC_VAR_MODULE:
216         case VLC_VAR_FILE:
217         case VLC_VAR_DIRECTORY:
218         case VLC_VAR_VARIABLE:
219             p_var->pf_cmp = CmpString;
220             p_var->pf_dup = DupString;
221             p_var->pf_free = FreeString;
222             p_var->val.psz_string = "";
223             break;
224         case VLC_VAR_FLOAT:
225             p_var->pf_cmp = CmpFloat;
226             p_var->val.f_float = 0.0;
227             break;
228         case VLC_VAR_TIME:
229             /* FIXME: TODO */
230             break;
231         case VLC_VAR_ADDRESS:
232             p_var->pf_cmp = CmpAddress;
233             p_var->val.p_address = NULL;
234             break;
235         case VLC_VAR_MUTEX:
236             p_var->pf_cmp = CmpAddress;
237             p_var->pf_free = FreeMutex;
238             p_var->val.p_address = malloc( sizeof(vlc_mutex_t) );
239             vlc_mutex_init( p_this, (vlc_mutex_t*)p_var->val.p_address );
240             break;
241         case VLC_VAR_LIST:
242             p_var->pf_cmp = CmpAddress;
243             p_var->pf_dup = DupList;
244             p_var->pf_free = FreeList;
245             p_var->val.p_list = &dummy_null_list;
246             break;
247     }
248
249     /* Duplicate the default data we stored. */
250     p_var->pf_dup( &p_var->val );
251
252     vlc_mutex_unlock( &p_this->var_lock );
253
254     return VLC_SUCCESS;
255 }
256
257 /*****************************************************************************
258  * var_Destroy: destroy a vlc variable
259  *****************************************************************************
260  * Look for the variable and destroy it if it is found. As in var_Create we
261  * do a call to memmove() but we have performance counterparts elsewhere.
262  *****************************************************************************/
263 int __var_Destroy( vlc_object_t *p_this, const char *psz_name )
264 {
265     int i_var, i;
266     variable_t *p_var;
267
268     vlc_mutex_lock( &p_this->var_lock );
269
270     i_var = GetUnused( p_this, psz_name );
271     if( i_var < 0 )
272     {
273         vlc_mutex_unlock( &p_this->var_lock );
274         return i_var;
275     }
276
277     p_var = &p_this->p_vars[i_var];
278
279     if( p_var->i_usage > 1 )
280     {
281         p_var->i_usage--;
282         vlc_mutex_unlock( &p_this->var_lock );
283         return VLC_SUCCESS;
284     }
285
286     /* Free value if needed */
287     p_var->pf_free( &p_var->val );
288
289     /* Free choice list if needed */
290     if( p_var->choices.i_count )
291     {
292         for( i = 0 ; i < p_var->choices.i_count ; i++ )
293         {
294             p_var->pf_free( &p_var->choices.p_values[i] );
295         }
296         free( p_var->choices.p_values );
297     }
298
299     /* Free callbacks if needed */
300     if( p_var->p_entries )
301     {
302         free( p_var->p_entries );
303     }
304
305     free( p_var->psz_name );
306     if( p_var->psz_text ) free( p_var->psz_text );
307
308     memmove( p_this->p_vars + i_var,
309              p_this->p_vars + i_var + 1,
310              (p_this->i_vars - i_var - 1) * sizeof(variable_t) );
311
312     if( (p_this->i_vars & 15) == 0 )
313     {
314         p_this->p_vars = realloc( p_this->p_vars,
315                           (p_this->i_vars) * sizeof( variable_t ) );
316     }
317
318     p_this->i_vars--;
319
320     vlc_mutex_unlock( &p_this->var_lock );
321
322     return VLC_SUCCESS;
323 }
324
325 /*****************************************************************************
326  * var_Change: perform an action on a variable
327  *****************************************************************************
328  *
329  *****************************************************************************/
330 int __var_Change( vlc_object_t *p_this, const char *psz_name,
331                   int i_action, vlc_value_t *p_val, vlc_value_t *p_val2 )
332 {
333     int i_var, i;
334     variable_t *p_var;
335     vlc_value_t oldval;
336
337     vlc_mutex_lock( &p_this->var_lock );
338
339     i_var = Lookup( p_this->p_vars, p_this->i_vars, psz_name );
340
341     if( i_var < 0 )
342     {
343         vlc_mutex_unlock( &p_this->var_lock );
344         return VLC_ENOVAR;
345     }
346
347     p_var = &p_this->p_vars[i_var];
348
349     switch( i_action )
350     {
351         case VLC_VAR_SETMIN:
352             if( p_var->i_type & VLC_VAR_HASMIN )
353             {
354                 p_var->pf_free( &p_var->min );
355             }
356             p_var->i_type |= VLC_VAR_HASMIN;
357             p_var->min = *p_val;
358             p_var->pf_dup( &p_var->min );
359             CheckValue( p_var, &p_var->val );
360             break;
361         case VLC_VAR_SETMAX:
362             if( p_var->i_type & VLC_VAR_HASMAX )
363             {
364                 p_var->pf_free( &p_var->max );
365             }
366             p_var->i_type |= VLC_VAR_HASMAX;
367             p_var->max = *p_val;
368             p_var->pf_dup( &p_var->max );
369             CheckValue( p_var, &p_var->val );
370             break;
371         case VLC_VAR_SETSTEP:
372             if( p_var->i_type & VLC_VAR_HASSTEP )
373             {
374                 p_var->pf_free( &p_var->step );
375             }
376             p_var->i_type |= VLC_VAR_HASSTEP;
377             p_var->step = *p_val;
378             p_var->pf_dup( &p_var->step );
379             CheckValue( p_var, &p_var->val );
380             break;
381         case VLC_VAR_ADDCHOICE:
382             /* FIXME: the list is sorted, dude. Use something cleverer. */
383             for( i = p_var->choices.i_count ; i-- ; )
384             {
385                 if( p_var->pf_cmp( p_var->choices.p_values[i], *p_val ) < 0 )
386                 {
387                     break;
388                 }
389             }
390
391             /* The new place is i+1 */
392             i++;
393
394             if( p_var->i_default >= i )
395             {
396                 p_var->i_default++;
397             }
398
399             INSERT_ELEM( p_var->choices.p_values, p_var->choices.i_count,
400                          i, *p_val );
401             INSERT_ELEM( p_var->choices_text.p_values,
402                          p_var->choices_text.i_count, i, (vlc_value_t)0 );
403             p_var->pf_dup( &p_var->choices.p_values[i] );
404             p_var->choices_text.p_values[i].psz_string =
405                 ( p_val2 && p_val2->psz_string ) ?
406                 strdup( p_val2->psz_string ) : NULL;
407
408             CheckValue( p_var, &p_var->val );
409             break;
410         case VLC_VAR_DELCHOICE:
411             /* FIXME: the list is sorted, dude. Use something cleverer. */
412             for( i = 0 ; i < p_var->choices.i_count ; i++ )
413             {
414                 if( p_var->pf_cmp( p_var->choices.p_values[i], *p_val ) == 0 )
415                 {
416                     break;
417                 }
418             }
419
420             if( i == p_var->choices.i_count )
421             {
422                 /* Not found */
423                 vlc_mutex_unlock( &p_this->var_lock );
424                 return VLC_EGENERIC;
425             }
426
427             if( p_var->i_default > i )
428             {
429                 p_var->i_default--;
430             }
431             else if( p_var->i_default == i )
432             {
433                 p_var->i_default = -1;
434             }
435
436             p_var->pf_free( &p_var->choices.p_values[i] );
437             if( p_var->choices_text.p_values[i].psz_string )
438                 free( p_var->choices_text.p_values[i].psz_string );
439             REMOVE_ELEM( p_var->choices.p_values, p_var->choices.i_count, i );
440             REMOVE_ELEM( p_var->choices_text.p_values,
441                          p_var->choices_text.i_count, i );
442
443             CheckValue( p_var, &p_var->val );
444             break;
445         case VLC_VAR_CHOICESCOUNT:
446             p_val->i_int = p_var->choices.i_count;
447             break;
448         case VLC_VAR_CLEARCHOICES:
449             for( i = 0 ; i < p_var->choices.i_count ; i++ )
450             {
451                 p_var->pf_free( &p_var->choices.p_values[i] );
452             }
453             if( p_var->choices.i_count )
454                 free( p_var->choices.p_values );
455
456             p_var->choices.i_count = 0;
457             p_var->choices.p_values = NULL;
458             p_var->i_default = -1;
459             break;
460         case VLC_VAR_SETDEFAULT:
461             /* FIXME: the list is sorted, dude. Use something cleverer. */
462             for( i = 0 ; i < p_var->choices.i_count ; i++ )
463             {
464                 if( p_var->pf_cmp( p_var->choices.p_values[i], *p_val ) == 0 )
465                 {
466                     break;
467                 }
468             }
469
470             if( i == p_var->choices.i_count )
471             {
472                 /* Not found */
473                 break;
474             }
475
476             p_var->i_default = i;
477             CheckValue( p_var, &p_var->val );
478             break;
479         case VLC_VAR_SETVALUE:
480             /* Duplicate data if needed */
481             p_var->pf_dup( p_val );
482             /* Backup needed stuff */
483             oldval = p_var->val;
484             /* Check boundaries and list */
485             CheckValue( p_var, p_val );
486             /* Set the variable */
487             p_var->val = *p_val;
488             /* Free data if needed */
489             p_var->pf_free( &oldval );
490             break;
491         case VLC_VAR_GETCHOICES:
492         case VLC_VAR_GETLIST:
493             p_val->p_list = malloc( sizeof(vlc_list_t) );
494             if( p_val2 ) p_val2->p_list = malloc( sizeof(vlc_list_t) );
495             if( p_var->choices.i_count )
496             {
497                 p_val->p_list->p_values = malloc( p_var->choices.i_count
498                                                   * sizeof(vlc_value_t) );
499                 p_val->p_list->pi_types = malloc( p_var->choices.i_count
500                                                   * sizeof(int) );
501                 if( p_val2 )
502                 {
503                     p_val2->p_list->p_values =
504                         malloc( p_var->choices.i_count * sizeof(vlc_value_t) );
505                     p_val2->p_list->pi_types =
506                         malloc( p_var->choices.i_count * sizeof(int) );
507                 }
508             }
509             p_val->p_list->i_count = p_var->choices.i_count;
510             if( p_val2 ) p_val2->p_list->i_count = p_var->choices.i_count;
511             for( i = 0 ; i < p_var->choices.i_count ; i++ )
512             {
513                 p_val->p_list->p_values[i] = p_var->choices.p_values[i];
514                 p_val->p_list->pi_types[i] = p_var->i_type;
515                 p_var->pf_dup( &p_val->p_list->p_values[i] );
516                 if( p_val2 )
517                 {
518                     p_val2->p_list->p_values[i].psz_string =
519                         p_var->choices_text.p_values[i].psz_string ?
520                     strdup(p_var->choices_text.p_values[i].psz_string) : NULL;
521                     p_val2->p_list->pi_types[i] = VLC_VAR_STRING;
522                 }
523             }
524             break;
525         case VLC_VAR_FREELIST:
526             FreeList( p_val );
527             break;
528         case VLC_VAR_SETTEXT:
529             if( p_var->psz_text ) free( p_var->psz_text );
530             if( p_val && p_val->psz_string )
531                 p_var->psz_text = strdup( p_val->psz_string );
532             break;
533         case VLC_VAR_GETTEXT:
534             p_val->psz_string = NULL;
535             if( p_var->psz_text )
536             {
537                 p_val->psz_string = strdup( p_var->psz_text );
538             }
539             break;
540
541         default:
542             break;
543     }
544
545     vlc_mutex_unlock( &p_this->var_lock );
546
547     return VLC_SUCCESS;
548 }
549
550 /*****************************************************************************
551  * var_Type: request a variable's type
552  *****************************************************************************
553  * This function returns the variable type if it exists, or 0 if the
554  * variable could not be found.
555  *****************************************************************************/
556 int __var_Type( vlc_object_t *p_this, const char *psz_name )
557 {
558     int i_var, i_type;
559
560     vlc_mutex_lock( &p_this->var_lock );
561
562     i_var = Lookup( p_this->p_vars, p_this->i_vars, psz_name );
563
564     if( i_var < 0 )
565     {
566         vlc_mutex_unlock( &p_this->var_lock );
567         return 0;
568     }
569
570     i_type = p_this->p_vars[i_var].i_type;
571
572     vlc_mutex_unlock( &p_this->var_lock );
573
574     return i_type;
575 }
576
577 /*****************************************************************************
578  * var_Set: set a variable's value
579  *****************************************************************************
580  *
581  *****************************************************************************/
582 int __var_Set( vlc_object_t *p_this, const char *psz_name, vlc_value_t val )
583 {
584     int i_var;
585     variable_t *p_var;
586     vlc_value_t oldval;
587
588     vlc_mutex_lock( &p_this->var_lock );
589
590     i_var = GetUnused( p_this, psz_name );
591     if( i_var < 0 )
592     {
593         vlc_mutex_unlock( &p_this->var_lock );
594         return i_var;
595     }
596
597     p_var = &p_this->p_vars[i_var];
598
599     /* Duplicate data if needed */
600     p_var->pf_dup( &val );
601
602     /* Backup needed stuff */
603     oldval = p_var->val;
604
605     /* Check boundaries and list */
606     CheckValue( p_var, &val );
607
608     /* Set the variable */
609     p_var->val = val;
610
611     /* Deal with callbacks. Tell we're in a callback, release the lock,
612      * call stored functions, retake the lock. */
613     if( p_var->i_entries )
614     {
615         int i_var;
616         int i_entries = p_var->i_entries;
617         callback_entry_t *p_entries = p_var->p_entries;
618
619         p_var->b_incallback = VLC_TRUE;
620         vlc_mutex_unlock( &p_this->var_lock );
621
622         /* The real calls */
623         for( ; i_entries-- ; )
624         {
625             p_entries[i_entries].pf_callback( p_this, psz_name, oldval, val,
626                                               p_entries[i_entries].p_data );
627         }
628
629         vlc_mutex_lock( &p_this->var_lock );
630
631         i_var = Lookup( p_this->p_vars, p_this->i_vars, psz_name );
632         if( i_var < 0 )
633         {
634             msg_Err( p_this, "variable %s has disappeared", psz_name );
635             vlc_mutex_unlock( &p_this->var_lock );
636             return VLC_ENOVAR;
637         }
638
639         p_var = &p_this->p_vars[i_var];
640         p_var->b_incallback = VLC_FALSE;
641     }
642
643     /* Free data if needed */
644     p_var->pf_free( &oldval );
645
646     vlc_mutex_unlock( &p_this->var_lock );
647
648     return VLC_SUCCESS;
649 }
650
651 /*****************************************************************************
652  * var_Get: get a variable's value
653  *****************************************************************************
654  *
655  *****************************************************************************/
656 int __var_Get( vlc_object_t *p_this, const char *psz_name, vlc_value_t *p_val )
657 {
658     int i_var;
659     variable_t *p_var;
660
661     vlc_mutex_lock( &p_this->var_lock );
662
663     i_var = Lookup( p_this->p_vars, p_this->i_vars, psz_name );
664
665     if( i_var < 0 )
666     {
667         vlc_mutex_unlock( &p_this->var_lock );
668         return VLC_ENOVAR;
669     }
670
671     p_var = &p_this->p_vars[i_var];
672
673     /* Really get the variable */
674     *p_val = p_var->val;
675
676     /* Duplicate value if needed */
677     p_var->pf_dup( p_val );
678
679     vlc_mutex_unlock( &p_this->var_lock );
680
681     return VLC_SUCCESS;
682 }
683
684 /*****************************************************************************
685  * var_AddCallback: register a callback in a variable
686  *****************************************************************************
687  * We store a function pointer pf_callback that will be called upon variable
688  * modification. p_data is a generic pointer that will be passed as additional
689  * argument to the callback function.
690  *****************************************************************************/
691 int __var_AddCallback( vlc_object_t *p_this, const char *psz_name,
692                        vlc_callback_t pf_callback, void *p_data )
693 {
694     int i_var;
695     variable_t *p_var;
696     callback_entry_t entry;
697
698     entry.pf_callback = pf_callback;
699     entry.p_data = p_data;
700
701     vlc_mutex_lock( &p_this->var_lock );
702
703     i_var = GetUnused( p_this, psz_name );
704     if( i_var < 0 )
705     {
706         vlc_mutex_unlock( &p_this->var_lock );
707         return i_var;
708     }
709
710     p_var = &p_this->p_vars[i_var];
711
712     INSERT_ELEM( p_var->p_entries,
713                  p_var->i_entries,
714                  p_var->i_entries,
715                  entry );
716
717     vlc_mutex_unlock( &p_this->var_lock );
718
719     return VLC_SUCCESS;
720 }
721
722 /*****************************************************************************
723  * var_DelCallback: remove a callback from a variable
724  *****************************************************************************
725  * pf_callback and p_data have to be given again, because different objects
726  * might have registered the same callback function.
727  *****************************************************************************/
728 int __var_DelCallback( vlc_object_t *p_this, const char *psz_name,
729                        vlc_callback_t pf_callback, void *p_data )
730 {
731     int i_entry, i_var;
732     variable_t *p_var;
733
734     vlc_mutex_lock( &p_this->var_lock );
735
736     i_var = GetUnused( p_this, psz_name );
737     if( i_var < 0 )
738     {
739         vlc_mutex_unlock( &p_this->var_lock );
740         return i_var;
741     }
742
743     p_var = &p_this->p_vars[i_var];
744
745     for( i_entry = p_var->i_entries ; i_entry-- ; )
746     {
747         if( p_var->p_entries[i_entry].pf_callback == pf_callback
748             && p_var->p_entries[i_entry].p_data == p_data )
749         {
750             break;
751         }
752     }
753
754     if( i_entry < 0 )
755     {
756         vlc_mutex_unlock( &p_this->var_lock );
757         return VLC_EGENERIC;
758     }
759
760     REMOVE_ELEM( p_var->p_entries, p_var->i_entries, i_entry );
761
762     vlc_mutex_unlock( &p_this->var_lock );
763
764     return VLC_SUCCESS;
765 }
766
767 /* Following functions are local */
768
769 /*****************************************************************************
770  * GetUnused: find an unused variable from its name
771  *****************************************************************************
772  * We do i_tries tries before giving up, just in case the variable is being
773  * modified and called from a callback.
774  *****************************************************************************/
775 static int GetUnused( vlc_object_t *p_this, const char *psz_name )
776 {
777     int i_var, i_tries = 0;
778
779     while( VLC_TRUE )
780     {
781         i_var = Lookup( p_this->p_vars, p_this->i_vars, psz_name );
782         if( i_var < 0 )
783         {
784             return VLC_ENOVAR;
785         }
786
787         if( ! p_this->p_vars[i_var].b_incallback )
788         {
789             return i_var;
790         }
791
792         if( i_tries++ > 100 )
793         {
794             msg_Err( p_this, "caught in a callback deadlock?" );
795             return VLC_ETIMEOUT;
796         }
797
798         vlc_mutex_unlock( &p_this->var_lock );
799         msleep( THREAD_SLEEP );
800         vlc_mutex_lock( &p_this->var_lock );
801     }
802 }
803
804 /*****************************************************************************
805  * HashString: our cool hash function
806  *****************************************************************************
807  * This function is not intended to be crypto-secure, we only want it to be
808  * fast and not suck too much. This one is pretty fast and did 0 collisions
809  * in wenglish's dictionary.
810  *****************************************************************************/
811 static uint32_t HashString( const char *psz_string )
812 {
813     uint32_t i_hash = 0;
814
815     while( *psz_string )
816     {
817         i_hash += *psz_string++;
818         i_hash += i_hash << 10;
819         i_hash ^= i_hash >> 8;
820     }
821
822     return i_hash;
823 }
824
825 /*****************************************************************************
826  * Insert: find an empty slot to insert a new variable
827  *****************************************************************************
828  * We use a recursive inner function indexed on the hash. This function does
829  * nothing in the rare cases where a collision may occur, see Lookup()
830  * to see how we handle them.
831  * XXX: does this really need to be written recursively?
832  *****************************************************************************/
833 static int Insert( variable_t *p_vars, int i_count, const char *psz_name )
834 {
835     if( i_count == 0 )
836     {
837         return 0;
838     }
839
840     return InsertInner( p_vars, i_count, HashString( psz_name ) );
841 }
842
843 static int InsertInner( variable_t *p_vars, int i_count, uint32_t i_hash )
844 {
845     int i_middle;
846
847     if( i_hash <= p_vars[0].i_hash )
848     {
849         return 0;
850     }
851
852     if( i_hash >= p_vars[i_count - 1].i_hash )
853     {
854         return i_count;
855     }
856
857     i_middle = i_count / 2;
858
859     /* We know that 0 < i_middle */
860     if( i_hash < p_vars[i_middle].i_hash )
861     {
862         return InsertInner( p_vars, i_middle, i_hash );
863     }
864
865     /* We know that i_middle + 1 < i_count */
866     if( i_hash > p_vars[i_middle + 1].i_hash )
867     {
868         return i_middle + 1 + InsertInner( p_vars + i_middle + 1,
869                                            i_count - i_middle - 1,
870                                            i_hash );
871     }
872
873     return i_middle + 1;
874 }
875
876 /*****************************************************************************
877  * Lookup: find an existing variable given its name
878  *****************************************************************************
879  * We use a recursive inner function indexed on the hash. Care is taken of
880  * possible hash collisions.
881  * XXX: does this really need to be written recursively?
882  *****************************************************************************/
883 static int Lookup( variable_t *p_vars, int i_count, const char *psz_name )
884 {
885     uint32_t i_hash;
886     int i, i_pos;
887
888     if( i_count == 0 )
889     {
890         return -1;
891     }
892
893     i_hash = HashString( psz_name );
894
895     i_pos = LookupInner( p_vars, i_count, i_hash );
896
897     /* Hash not found */
898     if( i_hash != p_vars[i_pos].i_hash )
899     {
900         return -1;
901     }
902
903     /* Hash found, entry found */
904     if( !strcmp( psz_name, p_vars[i_pos].psz_name ) )
905     {
906         return i_pos;
907     }
908
909     /* Hash collision! This should be very rare, but we cannot guarantee
910      * it will never happen. Just do an exhaustive search amongst all
911      * entries with the same hash. */
912     for( i = i_pos - 1 ; i > 0 && i_hash == p_vars[i].i_hash ; i-- )
913     {
914         if( !strcmp( psz_name, p_vars[i].psz_name ) )
915         {
916             return i;
917         }
918     }
919
920     for( i = i_pos + 1 ; i < i_count && i_hash == p_vars[i].i_hash ; i++ )
921     {
922         if( !strcmp( psz_name, p_vars[i].psz_name ) )
923         {
924             return i;
925         }
926     }
927
928     /* Hash found, but entry not found */
929     return -1;
930 }
931
932 static int LookupInner( variable_t *p_vars, int i_count, uint32_t i_hash )
933 {
934     int i_middle;
935
936     if( i_hash <= p_vars[0].i_hash )
937     {
938         return 0;
939     }
940
941     if( i_hash >= p_vars[i_count-1].i_hash )
942     {
943         return i_count - 1;
944     }
945
946     i_middle = i_count / 2;
947
948     /* We know that 0 < i_middle */
949     if( i_hash < p_vars[i_middle].i_hash )
950     {
951         return LookupInner( p_vars, i_middle, i_hash );
952     }
953
954     /* We know that i_middle + 1 < i_count */
955     if( i_hash > p_vars[i_middle].i_hash )
956     {
957         return i_middle + LookupInner( p_vars + i_middle,
958                                        i_count - i_middle,
959                                        i_hash );
960     }
961
962     return i_middle;
963 }
964
965 /*****************************************************************************
966  * CheckValue: check that a value is valid wrt. a variable
967  *****************************************************************************
968  * This function checks p_val's value against p_var's limitations such as
969  * minimal and maximal value, step, in-list position, and modifies p_val if
970  * necessary.
971  *****************************************************************************/
972 static void CheckValue ( variable_t *p_var, vlc_value_t *p_val )
973 {
974     /* Check that our variable is in the list */
975     if( p_var->i_type & VLC_VAR_HASCHOICE && p_var->choices.i_count )
976     {
977         int i;
978
979         /* FIXME: the list is sorted, dude. Use something cleverer. */
980         for( i = p_var->choices.i_count ; i-- ; )
981         {
982             if( p_var->pf_cmp( *p_val, p_var->choices.p_values[i] ) == 0 )
983             {
984                 break;
985             }
986         }
987
988         /* If not found, change it to anything vaguely valid */
989         if( i < 0 )
990         {
991             /* Free the old variable, get the new one, dup it */
992             p_var->pf_free( p_val );
993             *p_val = p_var->choices.p_values[p_var->i_default >= 0
994                                           ? p_var->i_default : 0 ];
995             p_var->pf_dup( p_val );
996         }
997     }
998
999     /* Check that our variable is within the bounds */
1000     switch( p_var->i_type & VLC_VAR_TYPE )
1001     {
1002         case VLC_VAR_INTEGER:
1003             if( p_var->i_type & VLC_VAR_HASSTEP && p_var->step.i_int
1004                  && (p_val->i_int % p_var->step.i_int) )
1005             {
1006                 p_val->i_int = (p_val->i_int + (p_var->step.i_int / 2))
1007                                / p_var->step.i_int * p_var->step.i_int;
1008             }
1009             if( p_var->i_type & VLC_VAR_HASMIN
1010                  && p_val->i_int < p_var->min.i_int )
1011             {
1012                 p_val->i_int = p_var->min.i_int;
1013             }
1014             if( p_var->i_type & VLC_VAR_HASMAX
1015                  && p_val->i_int > p_var->max.i_int )
1016             {
1017                 p_val->i_int = p_var->max.i_int;
1018             }
1019             break;
1020         case VLC_VAR_FLOAT:
1021             if( p_var->i_type & VLC_VAR_HASSTEP && p_var->step.f_float )
1022             {
1023                 float f_round = p_var->step.f_float * (float)(int)( 0.5 +
1024                                         p_val->f_float / p_var->step.f_float );
1025                 if( p_val->f_float != f_round )
1026                 {
1027                     p_val->f_float = f_round;
1028                 }
1029             }
1030             if( p_var->i_type & VLC_VAR_HASMIN
1031                  && p_val->f_float < p_var->min.f_float )
1032             {
1033                 p_val->f_float = p_var->min.f_float;
1034             }
1035             if( p_var->i_type & VLC_VAR_HASMAX
1036                  && p_val->f_float > p_var->max.f_float )
1037             {
1038                 p_val->f_float = p_var->max.f_float;
1039             }
1040             break;
1041         case VLC_VAR_TIME:
1042             /* FIXME: TODO */
1043             break;
1044     }
1045 }