variables.c 28.2 KB
Newer Older
1
2
3
4
/*****************************************************************************
 * variables.c: routines for object variables handling
 *****************************************************************************
 * Copyright (C) 2002 VideoLAN
gbazin's avatar
   
gbazin committed
5
 * $Id: variables.c,v 1.18 2002/12/14 19:34:06 gbazin Exp $
6
7
8
9
10
11
12
 *
 * Authors: Samuel Hocevar <sam@zoy.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
13
 *
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
#include <vlc/vlc.h>

#ifdef HAVE_STDLIB_H
#   include <stdlib.h>                                          /* realloc() */
#endif

33
34
35
36
37
38
39
40
41
/*****************************************************************************
 * Private types
 *****************************************************************************/
struct callback_entry_t
{
    vlc_callback_t pf_callback;
    void *         p_data;
};

42
43
44
/*****************************************************************************
 * Local comparison functions, returns 0 if v == w, < 0 if v < w, > 0 if v > w
 *****************************************************************************/
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
47
48
49
50
51
52
53
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; }
static int CmpString( vlc_value_t v, vlc_value_t w ) { return strcmp( v.psz_string, w.psz_string ); }
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; }
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; }

/*****************************************************************************
 * Local duplication functions, and local deallocation functions
 *****************************************************************************/
54
static void DupDummy( vlc_value_t *p_val ) { (void)p_val; /* unused */ }
55
56
static void DupString( vlc_value_t *p_val ) { p_val->psz_string = strdup( p_val->psz_string ); }

57
static void FreeDummy( vlc_value_t *p_val ) { (void)p_val; /* unused */ }
58
static void FreeString( vlc_value_t *p_val ) { free( p_val->psz_string ); }
gbazin's avatar
   
gbazin committed
59
static void FreeMutex( vlc_value_t *p_val ) { vlc_mutex_destroy( (vlc_mutex_t*)p_val->p_address ); free( p_val->p_address ); }
60

61
62
63
/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
64
65
66
67
68
69
static int      GetUnused   ( vlc_object_t *, const char * );
static uint32_t HashString  ( const char * );
static int      Insert      ( variable_t *, int, const char * );
static int      InsertInner ( variable_t *, int, uint32_t );
static int      Lookup      ( variable_t *, int, const char * );
static int      LookupInner ( variable_t *, int, uint32_t );
70

71
static void     CheckValue  ( variable_t *, vlc_value_t * );
72

73
74
75
76
77
78
79
/*****************************************************************************
 * var_Create: initialize a vlc variable
 *****************************************************************************
 * We hash the given string and insert it into the sorted list. The insertion
 * may require slow memory copies, but think about what we gain in the log(n)
 * lookup phase when setting/getting the variable value!
 *****************************************************************************/
80
int __var_Create( vlc_object_t *p_this, const char *psz_name, int i_type )
81
82
{
    int i_new;
83
    variable_t *p_var;
84
85
86

    vlc_mutex_lock( &p_this->var_lock );

87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
    /* FIXME: if the variable already exists, we don't duplicate it. But we
     * duplicate the lookups. It's not that serious, but if anyone finds some
     * time to rework Insert() so that only one lookup has to be done, feel
     * free to do so. */
    i_new = Lookup( p_this->p_vars, p_this->i_vars, psz_name );

    if( i_new >= 0 )
    {
        /* If the types differ, variable creation failed. */
        if( i_type != p_this->p_vars[i_new].i_type )
        {
            vlc_mutex_unlock( &p_this->var_lock );
            return VLC_EBADVAR;
        }

        p_this->p_vars[i_new].i_usage++;
        vlc_mutex_unlock( &p_this->var_lock );
        return VLC_SUCCESS;
    }

    i_new = Insert( p_this->p_vars, p_this->i_vars, psz_name );

109
110
111
112
113
114
115
116
117
118
    if( (p_this->i_vars & 15) == 15 )
    {
        p_this->p_vars = realloc( p_this->p_vars,
                                  (p_this->i_vars+17) * sizeof(variable_t) );
    }

    memmove( p_this->p_vars + i_new + 1,
             p_this->p_vars + i_new,
             (p_this->i_vars - i_new) * sizeof(variable_t) );

119
120
    p_this->i_vars++;

121
122
123
124
    p_var = &p_this->p_vars[i_new];

    p_var->i_hash = HashString( psz_name );
    p_var->psz_name = strdup( psz_name );
125

126
127
    p_var->i_type = i_type;
    memset( &p_var->val, 0, sizeof(vlc_value_t) );
128

129
130
131
    p_var->pf_dup = DupDummy;
    p_var->pf_free = FreeDummy;

132
    p_var->i_usage = 1;
133

134
    p_var->i_default = -1;
gbazin's avatar
   
gbazin committed
135
136
    p_var->choices.i_count = 0;
    p_var->choices.p_values = NULL;
137

138
    p_var->b_incallback = VLC_FALSE;
139
140
141
    p_var->i_entries = 0;
    p_var->p_entries = NULL;

142
143
144
145
    /* Always initialize the variable, even if it is a list variable; this
     * will lead to errors if the variable is not initialized, but it will
     * not cause crashes in the variable handling. */
    switch( i_type & VLC_VAR_TYPE )
146
    {
147
        case VLC_VAR_BOOL:
148
            p_var->pf_cmp = CmpBool;
149
150
151
            p_var->val.b_bool = VLC_FALSE;
            break;
        case VLC_VAR_INTEGER:
152
            p_var->pf_cmp = CmpInt;
153
154
155
156
157
            p_var->val.i_int = 0;
            break;
        case VLC_VAR_STRING:
        case VLC_VAR_MODULE:
        case VLC_VAR_FILE:
158
159
160
161
            p_var->pf_cmp = CmpString;
            p_var->pf_dup = DupString;
            p_var->pf_free = FreeString;
            p_var->val.psz_string = "";
162
163
            break;
        case VLC_VAR_FLOAT:
164
            p_var->pf_cmp = CmpFloat;
165
166
167
            p_var->val.f_float = 0.0;
            break;
        case VLC_VAR_TIME:
168
            /* FIXME: TODO */
169
170
            break;
        case VLC_VAR_ADDRESS:
171
            p_var->pf_cmp = CmpAddress;
172
173
            p_var->val.p_address = NULL;
            break;
174
        case VLC_VAR_MUTEX:
175
            p_var->pf_cmp = CmpAddress;
gbazin's avatar
   
gbazin committed
176
            p_var->pf_free = FreeMutex;
177
178
179
180
            p_var->val.p_address = malloc( sizeof(vlc_mutex_t) );
            vlc_mutex_init( p_this, (vlc_mutex_t*)p_var->val.p_address );
            break;
    }
181

182
183
184
    /* Duplicate the default data we stored. */
    p_var->pf_dup( &p_var->val );

185
    vlc_mutex_unlock( &p_this->var_lock );
186
187

    return VLC_SUCCESS;
188
189
190
191
192
193
194
195
}

/*****************************************************************************
 * var_Destroy: destroy a vlc variable
 *****************************************************************************
 * Look for the variable and destroy it if it is found. As in var_Create we
 * do a call to memmove() but we have performance counterparts elsewhere.
 *****************************************************************************/
196
int __var_Destroy( vlc_object_t *p_this, const char *psz_name )
197
{
198
    int i_var, i;
199
    variable_t *p_var;
200
201
202

    vlc_mutex_lock( &p_this->var_lock );

203
204
    i_var = GetUnused( p_this, psz_name );
    if( i_var < 0 )
205
206
    {
        vlc_mutex_unlock( &p_this->var_lock );
207
        return i_var;
208
209
    }

210
    p_var = &p_this->p_vars[i_var];
211
212
213
214
215
216
217
218

    if( p_var->i_usage > 1 )
    {
        p_var->i_usage--;
        vlc_mutex_unlock( &p_this->var_lock );
        return VLC_SUCCESS;
    }

219
    /* Free value if needed */
220
    p_var->pf_free( &p_var->val );
221

222
    /* Free choice list if needed */
gbazin's avatar
   
gbazin committed
223
    if( p_var->choices.i_count )
224
    {
gbazin's avatar
   
gbazin committed
225
        for( i = 0 ; i < p_var->choices.i_count ; i++ )
226
        {
gbazin's avatar
   
gbazin committed
227
            p_var->pf_free( &p_var->choices.p_values[i] );
228
        }
gbazin's avatar
   
gbazin committed
229
        free( p_var->choices.p_values );
230
231
    }

232
233
234
235
236
237
    /* Free callbacks if needed */
    if( p_var->p_entries )
    {
        free( p_var->p_entries );
    }

238
    free( p_var->psz_name );
239

240
241
242
    memmove( p_this->p_vars + i_var,
             p_this->p_vars + i_var + 1,
             (p_this->i_vars - i_var - 1) * sizeof(variable_t) );
243
244
245
246
247
248
249
250
251
252

    if( (p_this->i_vars & 15) == 0 )
    {
        p_this->p_vars = realloc( p_this->p_vars,
                          (p_this->i_vars) * sizeof( variable_t ) );
    }

    p_this->i_vars--;

    vlc_mutex_unlock( &p_this->var_lock );
253
254
255
256

    return VLC_SUCCESS;
}

257
258
259
/*****************************************************************************
 * var_Change: perform an action on a variable
 *****************************************************************************
260
 *
261
262
263
264
 *****************************************************************************/
int __var_Change( vlc_object_t *p_this, const char *psz_name,
                  int i_action, vlc_value_t *p_val )
{
265
    int i_var, i;
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
    variable_t *p_var;

    vlc_mutex_lock( &p_this->var_lock );

    i_var = Lookup( p_this->p_vars, p_this->i_vars, psz_name );

    if( i_var < 0 )
    {
        vlc_mutex_unlock( &p_this->var_lock );
        return VLC_ENOVAR;
    }

    p_var = &p_this->p_vars[i_var];

    switch( i_action )
    {
        case VLC_VAR_SETMIN:
283
284
285
286
287
            if( p_var->i_type & VLC_VAR_HASMIN )
            {
                p_var->pf_free( &p_var->min );
            }
            p_var->i_type |= VLC_VAR_HASMIN;
288
            p_var->min = *p_val;
289
            p_var->pf_dup( &p_var->min );
290
291
292
            CheckValue( p_var, &p_var->val );
            break;
        case VLC_VAR_SETMAX:
293
294
295
296
297
            if( p_var->i_type & VLC_VAR_HASMAX )
            {
                p_var->pf_free( &p_var->max );
            }
            p_var->i_type |= VLC_VAR_HASMAX;
298
            p_var->max = *p_val;
299
            p_var->pf_dup( &p_var->max );
300
301
302
            CheckValue( p_var, &p_var->val );
            break;
        case VLC_VAR_SETSTEP:
303
304
305
306
307
            if( p_var->i_type & VLC_VAR_HASSTEP )
            {
                p_var->pf_free( &p_var->step );
            }
            p_var->i_type |= VLC_VAR_HASSTEP;
308
            p_var->step = *p_val;
309
310
311
312
313
            p_var->pf_dup( &p_var->step );
            CheckValue( p_var, &p_var->val );
            break;
        case VLC_VAR_ADDCHOICE:
            /* FIXME: the list is sorted, dude. Use something cleverer. */
gbazin's avatar
   
gbazin committed
314
            for( i = p_var->choices.i_count ; i-- ; )
315
            {
gbazin's avatar
   
gbazin committed
316
                if( p_var->pf_cmp( p_var->choices.p_values[i], *p_val ) < 0 )
317
318
319
320
321
322
323
324
325
326
327
328
329
                {
                    break;
                }
            }

            /* The new place is i+1 */
            i++;

            if( p_var->i_default >= i )
            {
                p_var->i_default++;
            }

gbazin's avatar
   
gbazin committed
330
331
332
            INSERT_ELEM( p_var->choices.p_values, p_var->choices.i_count,
                         i, *p_val );
            p_var->pf_dup( &p_var->choices.p_values[i] );
333

334
335
336
337
            CheckValue( p_var, &p_var->val );
            break;
        case VLC_VAR_DELCHOICE:
            /* FIXME: the list is sorted, dude. Use something cleverer. */
gbazin's avatar
   
gbazin committed
338
            for( i = 0 ; i < p_var->choices.i_count ; i++ )
339
            {
gbazin's avatar
   
gbazin committed
340
                if( p_var->pf_cmp( p_var->choices.p_values[i], *p_val ) == 0 )
341
342
343
344
345
                {
                    break;
                }
            }

gbazin's avatar
   
gbazin committed
346
            if( i == p_var->choices.i_count )
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
            {
                /* Not found */
                vlc_mutex_unlock( &p_this->var_lock );
                return VLC_EGENERIC;
            }

            if( p_var->i_default > i )
            {
                p_var->i_default--;
            }
            else if( p_var->i_default == i )
            {
                p_var->i_default = -1;
            }

gbazin's avatar
   
gbazin committed
362
363
            p_var->pf_free( &p_var->choices.p_values[i] );
            REMOVE_ELEM( p_var->choices.p_values, p_var->choices.i_count, i );
364
365
366
367
368

            CheckValue( p_var, &p_var->val );
            break;
        case VLC_VAR_SETDEFAULT:
            /* FIXME: the list is sorted, dude. Use something cleverer. */
gbazin's avatar
   
gbazin committed
369
            for( i = 0 ; i < p_var->choices.i_count ; i++ )
370
            {
gbazin's avatar
   
gbazin committed
371
                if( p_var->pf_cmp( p_var->choices.p_values[i], *p_val ) == 0 )
372
373
374
375
376
                {
                    break;
                }
            }

gbazin's avatar
   
gbazin committed
377
            if( i == p_var->choices.i_count )
378
379
380
381
382
383
            {
                /* Not found */
                break;
            }

            p_var->i_default = i;
384
385
386
            CheckValue( p_var, &p_var->val );
            break;

387
        case VLC_VAR_GETLIST:
gbazin's avatar
   
gbazin committed
388
389
390
391
392
            p_val->p_list = malloc( sizeof(vlc_list_t) );
            p_val->p_list->p_values = malloc( p_var->choices.i_count
                                              * sizeof(vlc_value_t) );
            p_val->p_list->i_count = p_var->choices.i_count;
            for( i = 0 ; i < p_var->choices.i_count ; i++ )
393
            {
gbazin's avatar
   
gbazin committed
394
395
                p_val->p_list->p_values[i] = p_var->choices.p_values[i];
                p_var->pf_dup( &p_val->p_list->p_values[i] );
396
397
398
            }
            break;
        case VLC_VAR_FREELIST:
gbazin's avatar
   
gbazin committed
399
            for( i = p_val->p_list->i_count ; i-- ; )
400
            {
gbazin's avatar
   
gbazin committed
401
                p_var->pf_free( &p_val->p_list->p_values[i] );
402
            }
gbazin's avatar
   
gbazin committed
403
404
            free( p_val->p_list->p_values );
	    free( p_val->p_list );
405
406
407
408
409
410
411
412
413
414
415
            break;

        default:
            break;
    }

    vlc_mutex_unlock( &p_this->var_lock );

    return VLC_SUCCESS;
}

416
/*****************************************************************************
417
 * var_Type: request a variable's type
418
 *****************************************************************************
gbazin's avatar
   
gbazin committed
419
 * This function returns the variable type if it exists, or 0 if the
420
 * variable could not be found.
421
422
423
424
425
426
427
428
429
430
431
432
 *****************************************************************************/
int __var_Type( vlc_object_t *p_this, const char *psz_name )
{
    int i_var, i_type;

    vlc_mutex_lock( &p_this->var_lock );

    i_var = Lookup( p_this->p_vars, p_this->i_vars, psz_name );

    if( i_var < 0 )
    {
        vlc_mutex_unlock( &p_this->var_lock );
gbazin's avatar
   
gbazin committed
433
        return 0;
434
435
436
437
438
439
440
    }

    i_type = p_this->p_vars[i_var].i_type;

    vlc_mutex_unlock( &p_this->var_lock );

    return i_type;
441
442
443
444
445
446
447
448
449
450
}

/*****************************************************************************
 * var_Set: set a variable's value
 *****************************************************************************
 *
 *****************************************************************************/
int __var_Set( vlc_object_t *p_this, const char *psz_name, vlc_value_t val )
{
    int i_var;
451
452
    variable_t *p_var;
    vlc_value_t oldval;
453
454
455

    vlc_mutex_lock( &p_this->var_lock );

456
    i_var = GetUnused( p_this, psz_name );
457
458
459
    if( i_var < 0 )
    {
        vlc_mutex_unlock( &p_this->var_lock );
460
        return i_var;
461
462
    }

463
464
465
    p_var = &p_this->p_vars[i_var];

    /* Duplicate data if needed */
466
    p_var->pf_dup( &val );
467

468
469
    /* Backup needed stuff */
    oldval = p_var->val;
470

471
    /* Check boundaries and list */
472
473
    CheckValue( p_var, &val );

474
475
476
    /* Set the variable */
    p_var->val = val;

477
478
    /* Deal with callbacks. Tell we're in a callback, release the lock,
     * call stored functions, retake the lock. */
479
480
    if( p_var->i_entries )
    {
481
482
483
        int i_var;
        int i_entries = p_var->i_entries;
        callback_entry_t *p_entries = p_var->p_entries;
484

485
486
487
488
489
490
491
492
493
494
495
496
497
498
        p_var->b_incallback = VLC_TRUE;
        vlc_mutex_unlock( &p_this->var_lock );

        /* The real calls */
        for( ; i_entries-- ; )
        {
            p_entries[i_entries].pf_callback( p_this, psz_name, oldval, val,
                                              p_entries[i_entries].p_data );
        }

        vlc_mutex_lock( &p_this->var_lock );

        i_var = Lookup( p_this->p_vars, p_this->i_vars, psz_name );
        if( i_var < 0 )
499
        {
500
501
502
            msg_Err( p_this, "variable %s has disappeared" );
            vlc_mutex_unlock( &p_this->var_lock );
            return VLC_ENOVAR;
503
        }
504
505
506

        p_var = &p_this->p_vars[i_var];
        p_var->b_incallback = VLC_FALSE;
507
508
509
    }

    /* Free data if needed */
510
    p_var->pf_free( &oldval );
511
512
513
514
515
516
517
518
519
520
521

    vlc_mutex_unlock( &p_this->var_lock );

    return VLC_SUCCESS;
}

/*****************************************************************************
 * var_Get: get a variable's value
 *****************************************************************************
 *
 *****************************************************************************/
522
int __var_Get( vlc_object_t *p_this, const char *psz_name, vlc_value_t *p_val )
523
524
{
    int i_var;
525
    variable_t *p_var;
526
527
528
529
530
531
532
533

    vlc_mutex_lock( &p_this->var_lock );

    i_var = Lookup( p_this->p_vars, p_this->i_vars, psz_name );

    if( i_var < 0 )
    {
        vlc_mutex_unlock( &p_this->var_lock );
534
        return VLC_ENOVAR;
535
536
    }

537
    p_var = &p_this->p_vars[i_var];
538

539
    /* Really get the variable */
540
    *p_val = p_var->val;
541
542

    /* Duplicate value if needed */
543
    p_var->pf_dup( p_val );
544
545
546
547
548
549

    vlc_mutex_unlock( &p_this->var_lock );

    return VLC_SUCCESS;
}

550
551
552
553
554
555
556
557
558
559
/*****************************************************************************
 * var_AddCallback: register a callback in a variable
 *****************************************************************************
 * We store a function pointer pf_callback that will be called upon variable
 * modification. p_data is a generic pointer that will be passed as additional
 * argument to the callback function.
 *****************************************************************************/
int __var_AddCallback( vlc_object_t *p_this, const char *psz_name,
                       vlc_callback_t pf_callback, void *p_data )
{
560
    int i_var;
561
    variable_t *p_var;
562
    callback_entry_t entry;
563

564
565
    entry.pf_callback = pf_callback;
    entry.p_data = p_data;
566

567
568
    vlc_mutex_lock( &p_this->var_lock );

569
    i_var = GetUnused( p_this, psz_name );
570
571
572
    if( i_var < 0 )
    {
        vlc_mutex_unlock( &p_this->var_lock );
573
        return i_var;
574
575
576
577
    }

    p_var = &p_this->p_vars[i_var];

578
579
580
581
    INSERT_ELEM( p_var->p_entries,
                 p_var->i_entries,
                 p_var->i_entries,
                 entry );
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596

    vlc_mutex_unlock( &p_this->var_lock );

    return VLC_SUCCESS;
}

/*****************************************************************************
 * var_DelCallback: remove a callback from a variable
 *****************************************************************************
 * pf_callback and p_data have to be given again, because different objects
 * might have registered the same callback function.
 *****************************************************************************/
int __var_DelCallback( vlc_object_t *p_this, const char *psz_name,
                       vlc_callback_t pf_callback, void *p_data )
{
597
    int i_entry, i_var;
598
599
600
601
    variable_t *p_var;

    vlc_mutex_lock( &p_this->var_lock );

602
    i_var = GetUnused( p_this, psz_name );
603
604
605
    if( i_var < 0 )
    {
        vlc_mutex_unlock( &p_this->var_lock );
606
        return i_var;
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
    }

    p_var = &p_this->p_vars[i_var];

    for( i_entry = p_var->i_entries ; i_entry-- ; )
    {
        if( p_var->p_entries[i_entry].pf_callback == pf_callback
            || p_var->p_entries[i_entry].p_data == p_data )
        {
            break;
        }
    }

    if( i_entry < 0 )
    {
        vlc_mutex_unlock( &p_this->var_lock );
        return VLC_EGENERIC;
    }

626
    REMOVE_ELEM( p_var->p_entries, p_var->i_entries, i_entry );
627
628
629
630
631
632

    vlc_mutex_unlock( &p_this->var_lock );

    return VLC_SUCCESS;
}

633
634
/* Following functions are local */

635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
/*****************************************************************************
 * GetUnused: find an unused variable from its name
 *****************************************************************************
 * We do i_tries tries before giving up, just in case the variable is being
 * modified and called from a callback.
 *****************************************************************************/
static int GetUnused( vlc_object_t *p_this, const char *psz_name )
{
    int i_var, i_tries = 0;

    while( VLC_TRUE )
    {
        i_var = Lookup( p_this->p_vars, p_this->i_vars, psz_name );
        if( i_var < 0 )
        {
            return VLC_ENOVAR;
        }

        if( ! p_this->p_vars[i_var].b_incallback )
        {
            return i_var;
        }

        if( i_tries++ > 100 )
        {
            msg_Err( p_this, "caught in a callback deadlock?" );
            return VLC_ETIMEOUT;
        }

        vlc_mutex_unlock( &p_this->var_lock );
        msleep( THREAD_SLEEP );
        vlc_mutex_lock( &p_this->var_lock );
    }
}

670
671
672
673
674
675
676
/*****************************************************************************
 * HashString: our cool hash function
 *****************************************************************************
 * This function is not intended to be crypto-secure, we only want it to be
 * fast and not suck too much. This one is pretty fast and did 0 collisions
 * in wenglish's dictionary.
 *****************************************************************************/
677
static uint32_t HashString( const char *psz_string )
678
{
679
    uint32_t i_hash = 0;
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708

    while( *psz_string )
    {
        i_hash += *psz_string++;
        i_hash += i_hash << 10;
        i_hash ^= i_hash >> 8;
    }

    return i_hash;
}

/*****************************************************************************
 * Insert: find an empty slot to insert a new variable
 *****************************************************************************
 * We use a recursive inner function indexed on the hash. This function does
 * nothing in the rare cases where a collision may occur, see Lookup()
 * to see how we handle them.
 * XXX: does this really need to be written recursively?
 *****************************************************************************/
static int Insert( variable_t *p_vars, int i_count, const char *psz_name )
{
    if( i_count == 0 )
    {
        return 0;
    }

    return InsertInner( p_vars, i_count, HashString( psz_name ) );
}

709
static int InsertInner( variable_t *p_vars, int i_count, uint32_t i_hash )
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
{
    int i_middle;

    if( i_hash <= p_vars[0].i_hash )
    {
        return 0;
    }

    if( i_hash >= p_vars[i_count - 1].i_hash )
    {
        return i_count;
    }

    i_middle = i_count / 2;

    /* We know that 0 < i_middle */
    if( i_hash < p_vars[i_middle].i_hash )
    {
        return InsertInner( p_vars, i_middle, i_hash );
    }

    /* We know that i_middle + 1 < i_count */
    if( i_hash > p_vars[i_middle + 1].i_hash )
    {
        return i_middle + 1 + InsertInner( p_vars + i_middle + 1,
                                           i_count - i_middle - 1,
                                           i_hash );
    }

    return i_middle + 1;
}

/*****************************************************************************
 * Lookup: find an existing variable given its name
 *****************************************************************************
 * We use a recursive inner function indexed on the hash. Care is taken of
 * possible hash collisions.
 * XXX: does this really need to be written recursively?
 *****************************************************************************/
static int Lookup( variable_t *p_vars, int i_count, const char *psz_name )
{
751
    uint32_t i_hash;
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
    int i, i_pos;

    if( i_count == 0 )
    {
        return -1;
    }

    i_hash = HashString( psz_name );

    i_pos = LookupInner( p_vars, i_count, i_hash );

    /* Hash not found */
    if( i_hash != p_vars[i_pos].i_hash )
    {
        return -1;
    }

    /* Hash found, entry found */
    if( !strcmp( psz_name, p_vars[i_pos].psz_name ) )
    {
        return i_pos;
    }

    /* Hash collision! This should be very rare, but we cannot guarantee
     * it will never happen. Just do an exhaustive search amongst all
     * entries with the same hash. */
    for( i = i_pos - 1 ; i > 0 && i_hash == p_vars[i].i_hash ; i-- )
    {
        if( !strcmp( psz_name, p_vars[i].psz_name ) )
        {
            return i;
        }
    }

    for( i = i_pos + 1 ; i < i_count && i_hash == p_vars[i].i_hash ; i++ )
    {
        if( !strcmp( psz_name, p_vars[i].psz_name ) )
        {
            return i;
        }
    }

    /* Hash found, but entry not found */
    return -1;
}

798
static int LookupInner( variable_t *p_vars, int i_count, uint32_t i_hash )
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
{
    int i_middle;

    if( i_hash <= p_vars[0].i_hash )
    {
        return 0;
    }

    if( i_hash >= p_vars[i_count-1].i_hash )
    {
        return i_count - 1;
    }

    i_middle = i_count / 2;

    /* We know that 0 < i_middle */
    if( i_hash < p_vars[i_middle].i_hash )
    {
        return LookupInner( p_vars, i_middle, i_hash );
    }

    /* We know that i_middle + 1 < i_count */
    if( i_hash > p_vars[i_middle].i_hash )
    {
        return i_middle + LookupInner( p_vars + i_middle,
                                       i_count - i_middle,
                                       i_hash );
    }

    return i_middle;
}

831
/*****************************************************************************
832
 * CheckValue: check that a value is valid wrt. a variable
833
834
 *****************************************************************************
 * This function checks p_val's value against p_var's limitations such as
835
 * minimal and maximal value, step, in-list position, and modifies p_val if
836
837
838
839
 * necessary.
 *****************************************************************************/
static void CheckValue ( variable_t *p_var, vlc_value_t *p_val )
{
840
    /* Check that our variable is in the list */
gbazin's avatar
   
gbazin committed
841
    if( p_var->i_type & VLC_VAR_HASCHOICE && p_var->choices.i_count )
842
843
844
845
    {
        int i;

        /* FIXME: the list is sorted, dude. Use something cleverer. */
gbazin's avatar
   
gbazin committed
846
        for( i = p_var->choices.i_count ; i-- ; )
847
        {
gbazin's avatar
   
gbazin committed
848
            if( p_var->pf_cmp( *p_val, p_var->choices.p_values[i] ) == 0 )
849
850
851
852
853
854
855
856
857
858
            {
                break;
            }
        }

        /* If not found, change it to anything vaguely valid */
        if( i < 0 )
        {
            /* Free the old variable, get the new one, dup it */
            p_var->pf_free( p_val );
gbazin's avatar
   
gbazin committed
859
860
            *p_val = p_var->choices.p_values[p_var->i_default >= 0
                                          ? p_var->i_default : 0 ];
861
862
863
864
865
866
            p_var->pf_dup( p_val );
        }
    }

    /* Check that our variable is within the bounds */
    switch( p_var->i_type & VLC_VAR_TYPE )
867
868
    {
        case VLC_VAR_INTEGER:
869
870
            if( p_var->i_type & VLC_VAR_HASSTEP && p_var->step.i_int
                 && (p_val->i_int % p_var->step.i_int) )
871
872
873
874
            {
                p_val->i_int = (p_val->i_int + (p_var->step.i_int / 2))
                               / p_var->step.i_int * p_var->step.i_int;
            }
875
876
            if( p_var->i_type & VLC_VAR_HASMIN
                 && p_val->i_int < p_var->min.i_int )
877
878
879
            {
                p_val->i_int = p_var->min.i_int;
            }
880
881
            if( p_var->i_type & VLC_VAR_HASMAX
                 && p_val->i_int > p_var->max.i_int )
882
883
884
885
886
            {
                p_val->i_int = p_var->max.i_int;
            }
            break;
        case VLC_VAR_FLOAT:
887
            if( p_var->i_type & VLC_VAR_HASSTEP && p_var->step.f_float )
888
889
890
891
892
893
894
895
            {
                float f_round = p_var->step.f_float * (float)(int)( 0.5 +
                                        p_val->f_float / p_var->step.f_float );
                if( p_val->f_float != f_round )
                {
                    p_val->f_float = f_round;
                }
            }
896
897
            if( p_var->i_type & VLC_VAR_HASMIN
                 && p_val->f_float < p_var->min.f_float )
898
899
900
            {
                p_val->f_float = p_var->min.f_float;
            }
901
902
            if( p_var->i_type & VLC_VAR_HASMAX
                 && p_val->f_float > p_var->max.f_float )
903
904
905
906
907
            {
                p_val->f_float = p_var->max.f_float;
            }
            break;
        case VLC_VAR_TIME:
908
            /* FIXME: TODO */
909
910
911
            break;
    }
}