]> git.sesse.net Git - vlc/blob - src/misc/variables.c
* ./modules/misc/testsuite/test4.c: made the 4th test less CPU intensive
[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.3 2002/10/14 19:04:51 sam 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  * Local prototypes
35  *****************************************************************************/
36 static u32 HashString     ( const char * );
37 static int Insert         ( variable_t *, int, const char * );
38 static int InsertInner    ( variable_t *, int, u32 );
39 static int Lookup         ( variable_t *, int, const char * );
40 static int LookupInner    ( variable_t *, int, u32 );
41
42 /*****************************************************************************
43  * var_Create: initialize a vlc variable
44  *****************************************************************************
45  * We hash the given string and insert it into the sorted list. The insertion
46  * may require slow memory copies, but think about what we gain in the log(n)
47  * lookup phase when setting/getting the variable value!
48  *****************************************************************************/
49 int __var_Create( vlc_object_t *p_this, const char *psz_name, int i_type )
50 {
51     int i_new;
52     variable_t *p_var;
53
54     vlc_mutex_lock( &p_this->var_lock );
55
56     /* FIXME: if the variable already exists, we don't duplicate it. But we
57      * duplicate the lookups. It's not that serious, but if anyone finds some
58      * time to rework Insert() so that only one lookup has to be done, feel
59      * free to do so. */
60     i_new = Lookup( p_this->p_vars, p_this->i_vars, psz_name );
61
62     if( i_new >= 0 )
63     {
64         /* If the types differ, variable creation failed. */
65         if( i_type != p_this->p_vars[i_new].i_type )
66         {
67             vlc_mutex_unlock( &p_this->var_lock );
68             return VLC_EBADVAR;
69         }
70
71         p_this->p_vars[i_new].i_usage++;
72         vlc_mutex_unlock( &p_this->var_lock );
73         return VLC_SUCCESS;
74     }
75
76     i_new = Insert( p_this->p_vars, p_this->i_vars, psz_name );
77
78     if( (p_this->i_vars & 15) == 15 )
79     {
80         p_this->p_vars = realloc( p_this->p_vars,
81                                   (p_this->i_vars+17) * sizeof(variable_t) );
82     }
83
84     memmove( p_this->p_vars + i_new + 1,
85              p_this->p_vars + i_new,
86              (p_this->i_vars - i_new) * sizeof(variable_t) );
87
88     p_var = &p_this->p_vars[i_new];
89
90     p_var->i_hash = HashString( psz_name );
91     p_var->psz_name = strdup( psz_name );
92
93     p_var->i_type = i_type;
94     memset( &p_var->val, 0, sizeof(vlc_value_t) );
95
96     p_var->i_usage = 1;
97     p_var->b_set = VLC_FALSE;
98     p_var->b_active = VLC_TRUE;
99
100     p_this->i_vars++;
101
102     vlc_mutex_unlock( &p_this->var_lock );
103
104     return VLC_SUCCESS;
105 }
106
107 /*****************************************************************************
108  * var_Destroy: destroy a vlc variable
109  *****************************************************************************
110  * Look for the variable and destroy it if it is found. As in var_Create we
111  * do a call to memmove() but we have performance counterparts elsewhere.
112  *****************************************************************************/
113 int __var_Destroy( vlc_object_t *p_this, const char *psz_name )
114 {
115     int i_del;
116     variable_t *p_var;
117
118     vlc_mutex_lock( &p_this->var_lock );
119
120     i_del = Lookup( p_this->p_vars, p_this->i_vars, psz_name );
121
122     if( i_del < 0 )
123     {
124         msg_Err( p_this, "variable %s was not found", psz_name );
125         vlc_mutex_unlock( &p_this->var_lock );
126         return VLC_ENOVAR;
127     }
128
129     p_var = &p_this->p_vars[i_del];
130
131     if( p_var->i_usage > 1 )
132     {
133         p_var->i_usage--;
134         vlc_mutex_unlock( &p_this->var_lock );
135         return VLC_SUCCESS;
136     }
137
138     /* Free value if needed */
139     switch( p_var->i_type )
140     {
141         case VLC_VAR_STRING:
142         case VLC_VAR_MODULE:
143         case VLC_VAR_FILE:
144             if( p_var->b_set && p_var->val.psz_string )
145             {
146                 free( p_var->val.psz_string );
147             }
148             break;
149     }
150
151     free( p_var->psz_name );
152
153     memmove( p_this->p_vars + i_del,
154              p_this->p_vars + i_del + 1,
155              (p_this->i_vars - i_del - 1) * sizeof(variable_t) );
156
157     if( (p_this->i_vars & 15) == 0 )
158     {
159         p_this->p_vars = realloc( p_this->p_vars,
160                           (p_this->i_vars) * sizeof( variable_t ) );
161     }
162
163     p_this->i_vars--;
164
165     vlc_mutex_unlock( &p_this->var_lock );
166
167     return VLC_SUCCESS;
168 }
169
170 /*****************************************************************************
171  * var_Type: request a variable's type, 0 if not found
172  *****************************************************************************
173  * 
174  *****************************************************************************/
175 int __var_Type( vlc_object_t *p_this, const char *psz_name )
176 {
177     int i_var, i_type;
178
179     vlc_mutex_lock( &p_this->var_lock );
180
181     i_var = Lookup( p_this->p_vars, p_this->i_vars, psz_name );
182
183     if( i_var < 0 )
184     {
185         vlc_mutex_unlock( &p_this->var_lock );
186         return 0;
187     }
188
189     i_type = p_this->p_vars[i_var].i_type;
190
191     vlc_mutex_unlock( &p_this->var_lock );
192
193     return i_type;
194 }
195
196 /*****************************************************************************
197  * var_Set: set a variable's value
198  *****************************************************************************
199  *
200  *****************************************************************************/
201 int __var_Set( vlc_object_t *p_this, const char *psz_name, vlc_value_t val )
202 {
203     int i_var;
204
205     vlc_mutex_lock( &p_this->var_lock );
206
207     i_var = Lookup( p_this->p_vars, p_this->i_vars, psz_name );
208
209     if( i_var < 0 )
210     {
211         vlc_mutex_unlock( &p_this->var_lock );
212         return VLC_ENOVAR;
213     }
214
215     /* Duplicate value if needed */
216     switch( p_this->p_vars[i_var].i_type )
217     {
218         case VLC_VAR_STRING:
219         case VLC_VAR_MODULE:
220         case VLC_VAR_FILE:
221             if( p_this->p_vars[i_var].b_set
222                  && p_this->p_vars[i_var].val.psz_string )
223             {
224                 free( p_this->p_vars[i_var].val.psz_string );
225             }
226             if( val.psz_string )
227             {
228                 val.psz_string = strdup( val.psz_string );
229             }
230             break;
231     }
232
233     p_this->p_vars[i_var].val = val;
234     p_this->p_vars[i_var].b_set = VLC_TRUE;
235
236     /* XXX: callback stuff will go here */
237
238     vlc_mutex_unlock( &p_this->var_lock );
239
240     return VLC_SUCCESS;
241 }
242
243 /*****************************************************************************
244  * var_Get: get a variable's value
245  *****************************************************************************
246  *
247  *****************************************************************************/
248 int __var_Get( vlc_object_t *p_this, const char *psz_name, vlc_value_t *p_val )
249 {
250     int i_var;
251
252     vlc_mutex_lock( &p_this->var_lock );
253
254     i_var = Lookup( p_this->p_vars, p_this->i_vars, psz_name );
255
256     if( i_var < 0 )
257     {
258         vlc_mutex_unlock( &p_this->var_lock );
259         return VLC_ENOVAR;
260     }
261
262     if( !p_this->p_vars[i_var].b_set )
263     {
264         vlc_mutex_unlock( &p_this->var_lock );
265         return VLC_EBADVAR;
266     }
267
268     /* Some variables trigger special behaviour. */
269     switch( p_this->p_vars[i_var].i_type )
270     {
271         case VLC_VAR_COMMAND:
272             if( p_this->p_vars[i_var].b_set )
273             {
274                 int i_ret = ((int (*) (vlc_object_t *, char *, char *))
275                                 p_this->p_vars[i_var].val.p_address) (
276                                     p_this,
277                                     p_this->p_vars[i_var].psz_name,
278                                     p_val->psz_string );
279                 vlc_mutex_unlock( &p_this->var_lock );
280                 return i_ret;
281             }
282             break;
283     }
284
285     /* Really set the variable */
286     *p_val = p_this->p_vars[i_var].val;
287
288     /* Duplicate value if needed */
289     switch( p_this->p_vars[i_var].i_type )
290     {
291         case VLC_VAR_STRING:
292         case VLC_VAR_MODULE:
293         case VLC_VAR_FILE:
294             if( p_val->psz_string )
295             {
296                 p_val->psz_string = strdup( p_val->psz_string );
297             }
298             break;
299     }
300
301     vlc_mutex_unlock( &p_this->var_lock );
302
303     return VLC_SUCCESS;
304 }
305
306 /* Following functions are local */
307
308 /*****************************************************************************
309  * HashString: our cool hash function
310  *****************************************************************************
311  * This function is not intended to be crypto-secure, we only want it to be
312  * fast and not suck too much. This one is pretty fast and did 0 collisions
313  * in wenglish's dictionary.
314  *****************************************************************************/
315 static u32 HashString( const char *psz_string )
316 {
317     u32 i_hash = 0;
318
319     while( *psz_string )
320     {
321         i_hash += *psz_string++;
322         i_hash += i_hash << 10;
323         i_hash ^= i_hash >> 8;
324     }
325
326     return i_hash;
327 }
328
329 /*****************************************************************************
330  * Insert: find an empty slot to insert a new variable
331  *****************************************************************************
332  * We use a recursive inner function indexed on the hash. This function does
333  * nothing in the rare cases where a collision may occur, see Lookup()
334  * to see how we handle them.
335  * XXX: does this really need to be written recursively?
336  *****************************************************************************/
337 static int Insert( variable_t *p_vars, int i_count, const char *psz_name )
338 {
339     if( i_count == 0 )
340     {
341         return 0;
342     }
343
344     return InsertInner( p_vars, i_count, HashString( psz_name ) );
345 }
346
347 static int InsertInner( variable_t *p_vars, int i_count, u32 i_hash )
348 {
349     int i_middle;
350
351     if( i_hash <= p_vars[0].i_hash )
352     {
353         return 0;
354     }
355
356     if( i_hash >= p_vars[i_count - 1].i_hash )
357     {
358         return i_count;
359     }
360
361     i_middle = i_count / 2;
362
363     /* We know that 0 < i_middle */
364     if( i_hash < p_vars[i_middle].i_hash )
365     {
366         return InsertInner( p_vars, i_middle, i_hash );
367     }
368
369     /* We know that i_middle + 1 < i_count */
370     if( i_hash > p_vars[i_middle + 1].i_hash )
371     {
372         return i_middle + 1 + InsertInner( p_vars + i_middle + 1,
373                                            i_count - i_middle - 1,
374                                            i_hash );
375     }
376
377     return i_middle + 1;
378 }
379
380 /*****************************************************************************
381  * Lookup: find an existing variable given its name
382  *****************************************************************************
383  * We use a recursive inner function indexed on the hash. Care is taken of
384  * possible hash collisions.
385  * XXX: does this really need to be written recursively?
386  *****************************************************************************/
387 static int Lookup( variable_t *p_vars, int i_count, const char *psz_name )
388 {
389     u32 i_hash;
390     int i, i_pos;
391
392     if( i_count == 0 )
393     {
394         return -1;
395     }
396
397     i_hash = HashString( psz_name );
398
399     i_pos = LookupInner( p_vars, i_count, i_hash );
400
401     /* Hash not found */
402     if( i_hash != p_vars[i_pos].i_hash )
403     {
404         return -1;
405     }
406
407     /* Hash found, entry found */
408     if( !strcmp( psz_name, p_vars[i_pos].psz_name ) )
409     {
410         return i_pos;
411     }
412
413     /* Hash collision! This should be very rare, but we cannot guarantee
414      * it will never happen. Just do an exhaustive search amongst all
415      * entries with the same hash. */
416     for( i = i_pos - 1 ; i > 0 && i_hash == p_vars[i].i_hash ; i-- )
417     {
418         if( !strcmp( psz_name, p_vars[i].psz_name ) )
419         {
420             return i;
421         }
422     }
423
424     for( i = i_pos + 1 ; i < i_count && i_hash == p_vars[i].i_hash ; i++ )
425     {
426         if( !strcmp( psz_name, p_vars[i].psz_name ) )
427         {
428             return i;
429         }
430     }
431
432     /* Hash found, but entry not found */
433     return -1;
434 }
435
436 static int LookupInner( variable_t *p_vars, int i_count, u32 i_hash )
437 {
438     int i_middle;
439
440     if( i_hash <= p_vars[0].i_hash )
441     {
442         return 0;
443     }
444
445     if( i_hash >= p_vars[i_count-1].i_hash )
446     {
447         return i_count - 1;
448     }
449
450     i_middle = i_count / 2;
451
452     /* We know that 0 < i_middle */
453     if( i_hash < p_vars[i_middle].i_hash )
454     {
455         return LookupInner( p_vars, i_middle, i_hash );
456     }
457
458     /* We know that i_middle + 1 < i_count */
459     if( i_hash > p_vars[i_middle].i_hash )
460     {
461         return i_middle + LookupInner( p_vars + i_middle,
462                                        i_count - i_middle,
463                                        i_hash );
464     }
465
466     return i_middle;
467 }
468