1 /*****************************************************************************
2 * variables.c: routines for object variables handling
3 *****************************************************************************
4 * Copyright (C) 2002 VideoLAN
5 * $Id: variables.c,v 1.2 2002/10/14 16:46: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 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 );
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 )
53 vlc_mutex_lock( &p_this->var_lock );
55 if( (p_this->i_vars & 15) == 15 )
57 p_this->p_vars = realloc( p_this->p_vars,
58 (p_this->i_vars+17) * sizeof(variable_t) );
61 i_new = Insert( p_this->p_vars, p_this->i_vars, psz_name );
63 memmove( p_this->p_vars + i_new + 1,
64 p_this->p_vars + i_new,
65 (p_this->i_vars - i_new) * sizeof(variable_t) );
67 p_this->p_vars[i_new].i_hash = HashString( psz_name );
68 p_this->p_vars[i_new].psz_name = strdup( psz_name );
70 p_this->p_vars[i_new].i_type = i_type;
71 memset( &p_this->p_vars[i_new].val, 0, sizeof(vlc_value_t) );
73 p_this->p_vars[i_new].b_set = VLC_FALSE;
74 p_this->p_vars[i_new].b_active = VLC_TRUE;
78 vlc_mutex_unlock( &p_this->var_lock );
83 /*****************************************************************************
84 * var_Destroy: destroy a vlc variable
85 *****************************************************************************
86 * Look for the variable and destroy it if it is found. As in var_Create we
87 * do a call to memmove() but we have performance counterparts elsewhere.
88 *****************************************************************************/
89 int __var_Destroy( vlc_object_t *p_this, const char *psz_name )
93 vlc_mutex_lock( &p_this->var_lock );
95 i_del = Lookup( p_this->p_vars, p_this->i_vars, psz_name );
99 msg_Err( p_this, "variable %s was not found", psz_name );
100 vlc_mutex_unlock( &p_this->var_lock );
104 /* Free value if needed */
105 switch( p_this->p_vars[i_del].i_type )
110 if( p_this->p_vars[i_del].b_set
111 && p_this->p_vars[i_del].val.psz_string )
113 free( p_this->p_vars[i_del].val.psz_string );
118 free( p_this->p_vars[i_del].psz_name );
120 memmove( p_this->p_vars + i_del,
121 p_this->p_vars + i_del + 1,
122 (p_this->i_vars - i_del - 1) * sizeof(variable_t) );
124 if( (p_this->i_vars & 15) == 0 )
126 p_this->p_vars = realloc( p_this->p_vars,
127 (p_this->i_vars) * sizeof( variable_t ) );
132 vlc_mutex_unlock( &p_this->var_lock );
137 /*****************************************************************************
138 * var_Type: request a variable's type, 0 if not found
139 *****************************************************************************
141 *****************************************************************************/
142 int __var_Type( vlc_object_t *p_this, const char *psz_name )
146 vlc_mutex_lock( &p_this->var_lock );
148 i_var = Lookup( p_this->p_vars, p_this->i_vars, psz_name );
152 vlc_mutex_unlock( &p_this->var_lock );
156 i_type = p_this->p_vars[i_var].i_type;
158 vlc_mutex_unlock( &p_this->var_lock );
163 /*****************************************************************************
164 * var_Set: set a variable's value
165 *****************************************************************************
167 *****************************************************************************/
168 int __var_Set( vlc_object_t *p_this, const char *psz_name, vlc_value_t val )
172 vlc_mutex_lock( &p_this->var_lock );
174 i_var = Lookup( p_this->p_vars, p_this->i_vars, psz_name );
178 vlc_mutex_unlock( &p_this->var_lock );
182 /* Duplicate value if needed */
183 switch( p_this->p_vars[i_var].i_type )
188 if( p_this->p_vars[i_var].b_set
189 && p_this->p_vars[i_var].val.psz_string )
191 free( p_this->p_vars[i_var].val.psz_string );
195 val.psz_string = strdup( val.psz_string );
200 p_this->p_vars[i_var].val = val;
201 p_this->p_vars[i_var].b_set = VLC_TRUE;
203 /* XXX: callback stuff will go here */
205 vlc_mutex_unlock( &p_this->var_lock );
210 /*****************************************************************************
211 * var_Get: get a variable's value
212 *****************************************************************************
214 *****************************************************************************/
215 int __var_Get( vlc_object_t *p_this, const char *psz_name, vlc_value_t *p_val )
219 vlc_mutex_lock( &p_this->var_lock );
221 i_var = Lookup( p_this->p_vars, p_this->i_vars, psz_name );
225 vlc_mutex_unlock( &p_this->var_lock );
229 if( !p_this->p_vars[i_var].b_set )
231 vlc_mutex_unlock( &p_this->var_lock );
235 /* Some variables trigger special behaviour. */
236 switch( p_this->p_vars[i_var].i_type )
238 case VLC_VAR_COMMAND:
239 if( p_this->p_vars[i_var].b_set )
241 int i_ret = ((int (*) (vlc_object_t *, char *, char *))
242 p_this->p_vars[i_var].val.p_address) (
244 p_this->p_vars[i_var].psz_name,
246 vlc_mutex_unlock( &p_this->var_lock );
252 /* Really set the variable */
253 *p_val = p_this->p_vars[i_var].val;
255 /* Duplicate value if needed */
256 switch( p_this->p_vars[i_var].i_type )
261 if( p_val->psz_string )
263 p_val->psz_string = strdup( p_val->psz_string );
268 vlc_mutex_unlock( &p_this->var_lock );
273 /* Following functions are local */
275 /*****************************************************************************
276 * HashString: our cool hash function
277 *****************************************************************************
278 * This function is not intended to be crypto-secure, we only want it to be
279 * fast and not suck too much. This one is pretty fast and did 0 collisions
280 * in wenglish's dictionary.
281 *****************************************************************************/
282 static u32 HashString( const char *psz_string )
288 i_hash += *psz_string++;
289 i_hash += i_hash << 10;
290 i_hash ^= i_hash >> 8;
296 /*****************************************************************************
297 * Insert: find an empty slot to insert a new variable
298 *****************************************************************************
299 * We use a recursive inner function indexed on the hash. This function does
300 * nothing in the rare cases where a collision may occur, see Lookup()
301 * to see how we handle them.
302 * XXX: does this really need to be written recursively?
303 *****************************************************************************/
304 static int Insert( variable_t *p_vars, int i_count, const char *psz_name )
311 return InsertInner( p_vars, i_count, HashString( psz_name ) );
314 static int InsertInner( variable_t *p_vars, int i_count, u32 i_hash )
318 if( i_hash <= p_vars[0].i_hash )
323 if( i_hash >= p_vars[i_count - 1].i_hash )
328 i_middle = i_count / 2;
330 /* We know that 0 < i_middle */
331 if( i_hash < p_vars[i_middle].i_hash )
333 return InsertInner( p_vars, i_middle, i_hash );
336 /* We know that i_middle + 1 < i_count */
337 if( i_hash > p_vars[i_middle + 1].i_hash )
339 return i_middle + 1 + InsertInner( p_vars + i_middle + 1,
340 i_count - i_middle - 1,
347 /*****************************************************************************
348 * Lookup: find an existing variable given its name
349 *****************************************************************************
350 * We use a recursive inner function indexed on the hash. Care is taken of
351 * possible hash collisions.
352 * XXX: does this really need to be written recursively?
353 *****************************************************************************/
354 static int Lookup( variable_t *p_vars, int i_count, const char *psz_name )
364 i_hash = HashString( psz_name );
366 i_pos = LookupInner( p_vars, i_count, i_hash );
369 if( i_hash != p_vars[i_pos].i_hash )
374 /* Hash found, entry found */
375 if( !strcmp( psz_name, p_vars[i_pos].psz_name ) )
380 /* Hash collision! This should be very rare, but we cannot guarantee
381 * it will never happen. Just do an exhaustive search amongst all
382 * entries with the same hash. */
383 for( i = i_pos - 1 ; i > 0 && i_hash == p_vars[i].i_hash ; i-- )
385 if( !strcmp( psz_name, p_vars[i].psz_name ) )
391 for( i = i_pos + 1 ; i < i_count && i_hash == p_vars[i].i_hash ; i++ )
393 if( !strcmp( psz_name, p_vars[i].psz_name ) )
399 /* Hash found, but entry not found */
403 static int LookupInner( variable_t *p_vars, int i_count, u32 i_hash )
407 if( i_hash <= p_vars[0].i_hash )
412 if( i_hash >= p_vars[i_count-1].i_hash )
417 i_middle = i_count / 2;
419 /* We know that 0 < i_middle */
420 if( i_hash < p_vars[i_middle].i_hash )
422 return LookupInner( p_vars, i_middle, i_hash );
425 /* We know that i_middle + 1 < i_count */
426 if( i_hash > p_vars[i_middle].i_hash )
428 return i_middle + LookupInner( p_vars + i_middle,