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
,