* ./include/vlc/vlc.h, ./src/libvlc.c: added VLC_Error() to the libvlc API.
[vlc.git] / src / misc / variables.c
blob03fc83baaa162bbaa37880d87f554aed0fbc8758
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 /*****************************************************************************
25 * Preamble
26 *****************************************************************************/
27 #include <vlc/vlc.h>
29 #ifdef HAVE_STDLIB_H
30 # include <stdlib.h> /* realloc() */
31 #endif
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 );
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 )
51 int i_new;
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;
76 p_this->i_vars++;
78 vlc_mutex_unlock( &p_this->var_lock );
80 return VLC_SUCCESS;
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 )
91 int i_del;
93 vlc_mutex_lock( &p_this->var_lock );
95 i_del = Lookup( p_this->p_vars, p_this->i_vars, psz_name );
97 if( i_del < 0 )
99 msg_Err( p_this, "variable %s was not found", psz_name );
100 vlc_mutex_unlock( &p_this->var_lock );
101 return VLC_ENOVAR;
104 /* Free value if needed */
105 switch( p_this->p_vars[i_del].i_type )
107 case VLC_VAR_STRING:
108 case VLC_VAR_MODULE:
109 case VLC_VAR_FILE:
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 );
115 break;
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 ) );
130 p_this->i_vars--;
132 vlc_mutex_unlock( &p_this->var_lock );
134 return VLC_SUCCESS;
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 )
144 int i_var, i_type;
146 vlc_mutex_lock( &p_this->var_lock );
148 i_var = Lookup( p_this->p_vars, p_this->i_vars, psz_name );
150 if( i_var < 0 )
152 vlc_mutex_unlock( &p_this->var_lock );
153 return 0;
156 i_type = p_this->p_vars[i_var].i_type;
158 vlc_mutex_unlock( &p_this->var_lock );
160 return i_type;
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 )
170 int i_var;
172 vlc_mutex_lock( &p_this->var_lock );
174 i_var = Lookup( p_this->p_vars, p_this->i_vars, psz_name );
176 if( i_var < 0 )
178 vlc_mutex_unlock( &p_this->var_lock );
179 return VLC_ENOVAR;
182 /* Duplicate value if needed */
183 switch( p_this->p_vars[i_var].i_type )
185 case VLC_VAR_STRING:
186 case VLC_VAR_MODULE:
187 case VLC_VAR_FILE:
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 );
193 if( val.psz_string )
195 val.psz_string = strdup( val.psz_string );
197 break;
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 );
207 return VLC_SUCCESS;
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 )
217 int i_var;
219 vlc_mutex_lock( &p_this->var_lock );
221 i_var = Lookup( p_this->p_vars, p_this->i_vars, psz_name );
223 if( i_var < 0 )
225 vlc_mutex_unlock( &p_this->var_lock );
226 return VLC_ENOVAR;
229 if( !p_this->p_vars[i_var].b_set )
231 vlc_mutex_unlock( &p_this->var_lock );
232 return VLC_EBADVAR;
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) (
243 p_this,
244 p_this->p_vars[i_var].psz_name,
245 p_val->psz_string );
246 vlc_mutex_unlock( &p_this->var_lock );
247 return i_ret;
249 break;
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 )
258 case VLC_VAR_STRING:
259 case VLC_VAR_MODULE:
260 case VLC_VAR_FILE:
261 if( p_val->psz_string )
263 p_val->psz_string = strdup( p_val->psz_string );
265 break;
268 vlc_mutex_unlock( &p_this->var_lock );
270 return VLC_SUCCESS;
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 )
284 u32 i_hash = 0;
286 while( *psz_string )
288 i_hash += *psz_string++;
289 i_hash += i_hash << 10;
290 i_hash ^= i_hash >> 8;
293 return i_hash;
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 )
306 if( i_count == 0 )
308 return 0;
311 return InsertInner( p_vars, i_count, HashString( psz_name ) );
314 static int InsertInner( variable_t *p_vars, int i_count, u32 i_hash )
316 int i_middle;
318 if( i_hash <= p_vars[0].i_hash )
320 return 0;
323 if( i_hash >= p_vars[i_count - 1].i_hash )
325 return i_count;
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,
341 i_hash );
344 return 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 )
356 u32 i_hash;
357 int i, i_pos;
359 if( i_count == 0 )
361 return -1;
364 i_hash = HashString( psz_name );
366 i_pos = LookupInner( p_vars, i_count, i_hash );
368 /* Hash not found */
369 if( i_hash != p_vars[i_pos].i_hash )
371 return -1;
374 /* Hash found, entry found */
375 if( !strcmp( psz_name, p_vars[i_pos].psz_name ) )
377 return i_pos;
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 ) )
387 return i;
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 ) )
395 return i;
399 /* Hash found, but entry not found */
400 return -1;
403 static int LookupInner( variable_t *p_vars, int i_count, u32 i_hash )
405 int i_middle;
407 if( i_hash <= p_vars[0].i_hash )
409 return 0;
412 if( i_hash >= p_vars[i_count-1].i_hash )
414 return i_count - 1;
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,
429 i_count - i_middle,
430 i_hash );
433 return i_middle;