]> git.sesse.net Git - vlc/blob - include/vlc_arrays.h
udp: fix locking (fixes #14234) and cancellation
[vlc] / include / vlc_arrays.h
1 /*****************************************************************************
2  * vlc_arrays.h : Arrays and data structures handling
3  *****************************************************************************
4  * Copyright (C) 1999-2004 VLC authors and VideoLAN
5  * $Id$
6  *
7  * Authors: Samuel Hocevar <sam@zoy.org>
8  *          ClĂ©ment Stenac <zorglub@videolan.org>
9  *
10  * This program is free software; you can redistribute it and/or modify it
11  * under the terms of the GNU Lesser General Public License as published by
12  * the Free Software Foundation; either version 2.1 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public License
21  * along with this program; if not, write to the Free Software Foundation,
22  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 #ifndef VLC_ARRAYS_H_
26 #define VLC_ARRAYS_H_
27
28 /**
29  * \file
30  * This file defines functions, structures and macros for handling arrays in vlc
31  */
32
33 /* realloc() that never fails *if* downsizing */
34 static inline void *realloc_down( void *ptr, size_t size )
35 {
36     void *ret = realloc( ptr, size );
37     return ret ? ret : ptr;
38 }
39
40 /**
41  * Simple dynamic array handling. Array is realloced at each insert/removal
42  */
43 #define INSERT_ELEM( p_ar, i_oldsize, i_pos, elem )                           \
44     do                                                                        \
45     {                                                                         \
46         if( !(i_oldsize) ) (p_ar) = NULL;                                       \
47         (p_ar) = realloc( p_ar, ((i_oldsize) + 1) * sizeof(*(p_ar)) ); \
48         if( !(p_ar) ) abort();                                                \
49         if( (i_oldsize) - (i_pos) )                                           \
50         {                                                                     \
51             memmove( (p_ar) + (i_pos) + 1, (p_ar) + (i_pos),                  \
52                      ((i_oldsize) - (i_pos)) * sizeof( *(p_ar) ) );           \
53         }                                                                     \
54         (p_ar)[(i_pos)] = elem;                                                 \
55         (i_oldsize)++;                                                        \
56     }                                                                         \
57     while( 0 )
58
59 #define REMOVE_ELEM( p_ar, i_size, i_pos )                                    \
60     do                                                                        \
61     {                                                                         \
62         if( (i_size) - (i_pos) - 1 )                                          \
63         {                                                                     \
64             memmove( (p_ar) + (i_pos),                                        \
65                      (p_ar) + (i_pos) + 1,                                    \
66                      ((i_size) - (i_pos) - 1) * sizeof( *(p_ar) ) );          \
67         }                                                                     \
68         if( i_size > 1 )                                                      \
69             (p_ar) = realloc_down( p_ar, ((i_size) - 1) * sizeof( *(p_ar) ) );\
70         else                                                                  \
71         {                                                                     \
72             free( p_ar );                                                     \
73             (p_ar) = NULL;                                                    \
74         }                                                                     \
75         (i_size)--;                                                           \
76     }                                                                         \
77     while( 0 )
78
79 #define TAB_INIT( count, tab )                  \
80   do {                                          \
81     (count) = 0;                                \
82     (tab) = NULL;                               \
83   } while(0)
84
85 #define TAB_CLEAN( count, tab )                 \
86   do {                                          \
87     free( tab );                                \
88     (count)= 0;                                 \
89     (tab)= NULL;                                \
90   } while(0)
91
92 #define TAB_APPEND_CAST( cast, count, tab, p )             \
93   do {                                          \
94     if( (count) > 0 )                           \
95         (tab) = cast realloc( tab, sizeof( *(tab) ) * ( (count) + 1 ) ); \
96     else                                        \
97         (tab) = cast malloc( sizeof( *(tab) ) );    \
98     if( !(tab) ) abort();                       \
99     (tab)[count] = (p);                         \
100     (count)++;                                  \
101   } while(0)
102
103 #define TAB_APPEND( count, tab, p )             \
104     TAB_APPEND_CAST( , count, tab, p )
105
106 #define TAB_FIND( count, tab, p, idx )          \
107   do {                                          \
108     for( (idx) = 0; (idx) < (count); (idx)++ )  \
109         if( (tab)[(idx)] == (p) )               \
110             break;                              \
111     if( (idx) >= (count) )                      \
112         (idx) = -1;                             \
113   } while(0)
114
115
116 #define TAB_REMOVE( count, tab, p )             \
117   do {                                          \
118         int i_index;                            \
119         TAB_FIND( count, tab, p, i_index );     \
120         if( i_index >= 0 )                      \
121         {                                       \
122             if( (count) > 1 )                   \
123             {                                   \
124                 memmove( ((void**)(tab) + i_index),    \
125                          ((void**)(tab) + i_index+1),  \
126                          ( (count) - i_index - 1 ) * sizeof( *(tab) ) );\
127             }                                   \
128             (count)--;                          \
129             if( (count) == 0 )                  \
130             {                                   \
131                 free( tab );                    \
132                 (tab) = NULL;                   \
133             }                                   \
134         }                                       \
135   } while(0)
136
137 #define TAB_INSERT_CAST( cast, count, tab, p, index ) do { \
138     if( (count) > 0 )                           \
139         (tab) = cast realloc( tab, sizeof( *(tab) ) * ( (count) + 1 ) ); \
140     else                                        \
141         (tab) = cast malloc( sizeof( *(tab) ) );       \
142     if( !(tab) ) abort();                       \
143     if( (count) - (index) > 0 )                 \
144         memmove( (void**)(tab) + (index) + 1,   \
145                  (void**)(tab) + (index),       \
146                  ((count) - (index)) * sizeof(*(tab)) );\
147     (tab)[(index)] = (p);                       \
148     (count)++;                                  \
149 } while(0)
150
151 #define TAB_INSERT( count, tab, p, index )      \
152     TAB_INSERT_CAST( , count, tab, p, index )
153
154 /**
155  * Binary search in a sorted array. The key must be comparable by < and >
156  * \param entries array of entries
157  * \param count number of entries
158  * \param elem key to check within an entry (like .id, or ->i_id)
159  * \param zetype type of the key
160  * \param key value of the key
161  * \param answer index of answer within the array. -1 if not found
162  */
163 #define BSEARCH( entries, count, elem, zetype, key, answer ) \
164    do {  \
165     int low = 0, high = count - 1;   \
166     answer = -1; \
167     while( low <= high ) {\
168         int mid = (low + high ) / 2; /* Just don't care about 2^30 tables */ \
169         zetype mid_val = entries[mid] elem;\
170         if( mid_val < key ) \
171             low = mid + 1; \
172         else if ( mid_val > key ) \
173             high = mid -1;  \
174         else    \
175         {   \
176             answer = mid;  break;   \
177         }\
178     } \
179  } while(0)
180
181
182 /************************************************************************
183  * Dynamic arrays with progressive allocation
184  ************************************************************************/
185
186 /* Internal functions */
187 #define _ARRAY_ALLOC(array, newsize) {                                      \
188     (array).i_alloc = newsize;                                              \
189     (array).p_elems = realloc( (array).p_elems, (array).i_alloc *           \
190                                sizeof(*(array).p_elems) );                  \
191     if( !(array).p_elems ) abort();                                         \
192 }
193
194 #define _ARRAY_GROW1(array) {                                               \
195     if( (array).i_alloc < 10 )                                              \
196         _ARRAY_ALLOC(array, 10 )                                            \
197     else if( (array).i_alloc == (array).i_size )                            \
198         _ARRAY_ALLOC(array, (int)(array.i_alloc * 1.5) )                    \
199 }
200
201 #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
202
203 /* API */
204 #define DECL_ARRAY(type) struct {                                           \
205     int i_alloc;                                                            \
206     int i_size;                                                             \
207     type *p_elems;                                                          \
208 }
209
210 #define TYPEDEF_ARRAY(type, name) typedef DECL_ARRAY(type) name;
211
212 #define ARRAY_INIT(array)                                                   \
213   do {                                                                      \
214     (array).i_alloc = 0;                                                    \
215     (array).i_size = 0;                                                     \
216     (array).p_elems = NULL;                                                 \
217   } while(0)
218
219 #define ARRAY_RESET(array)                                                  \
220   do {                                                                      \
221     (array).i_alloc = 0;                                                    \
222     (array).i_size = 0;                                                     \
223     free( (array).p_elems ); (array).p_elems = NULL;                        \
224   } while(0)
225
226 #define ARRAY_APPEND(array, elem)                                           \
227   do {                                                                      \
228     _ARRAY_GROW1(array);                                                    \
229     (array).p_elems[(array).i_size] = elem;                                 \
230     (array).i_size++;                                                       \
231   } while(0)
232
233 #define ARRAY_INSERT(array,elem,pos)                                        \
234   do {                                                                      \
235     _ARRAY_GROW1(array);                                                    \
236     if( (array).i_size - pos ) {                                            \
237         memmove( (array).p_elems + pos + 1, (array).p_elems + pos,          \
238                  ((array).i_size-pos) * sizeof(*(array).p_elems) );         \
239     }                                                                       \
240     (array).p_elems[pos] = elem;                                            \
241     (array).i_size++;                                                       \
242   } while(0)
243
244 #define _ARRAY_SHRINK(array) {                                              \
245     if( (array).i_size > 10 && (array).i_size < (int)((array).i_alloc / 1.5) ) {  \
246         _ARRAY_ALLOC(array, (array).i_size + 5);                            \
247     }                                                                       \
248 }
249
250 #define ARRAY_REMOVE(array,pos)                                             \
251   do {                                                                      \
252     if( (array).i_size - (pos) - 1 )                                        \
253     {                                                                       \
254         memmove( (array).p_elems + pos, (array).p_elems + pos + 1,          \
255                  ( (array).i_size - pos - 1 ) *sizeof(*(array).p_elems) );  \
256     }                                                                       \
257     (array).i_size--;                                                       \
258     _ARRAY_SHRINK(array);                                                   \
259   } while(0)
260
261 #define ARRAY_VAL(array, pos) array.p_elems[pos]
262
263 #define ARRAY_BSEARCH(array, elem, zetype, key, answer) \
264     BSEARCH( (array).p_elems, (array).i_size, elem, zetype, key, answer)
265
266 #define FOREACH_ARRAY( item, array ) { \
267     int fe_idx; \
268     for( fe_idx = 0 ; fe_idx < (array).i_size ; fe_idx++ ) \
269     { \
270         item = (array).p_elems[fe_idx];
271
272 #define FOREACH_END() } }
273
274
275 /************************************************************************
276  * Dynamic arrays with progressive allocation (Preferred API)
277  ************************************************************************/
278 typedef struct vlc_array_t
279 {
280     int i_count;
281     void ** pp_elems;
282 } vlc_array_t;
283
284 static inline void vlc_array_init( vlc_array_t * p_array )
285 {
286     memset( p_array, 0, sizeof(vlc_array_t) );
287 }
288
289 static inline void vlc_array_clear( vlc_array_t * p_array )
290 {
291     free( p_array->pp_elems );
292     memset( p_array, 0, sizeof(vlc_array_t) );
293 }
294
295 static inline vlc_array_t * vlc_array_new( void )
296 {
297     vlc_array_t * ret = (vlc_array_t *)malloc( sizeof(vlc_array_t) );
298     if( ret ) vlc_array_init( ret );
299     return ret;
300 }
301
302 static inline void vlc_array_destroy( vlc_array_t * p_array )
303 {
304     if( !p_array )
305         return;
306     vlc_array_clear( p_array );
307     free( p_array );
308 }
309
310
311 /* Read */
312 static inline int
313 vlc_array_count( vlc_array_t * p_array )
314 {
315     return p_array->i_count;
316 }
317
318 static inline void *
319 vlc_array_item_at_index( vlc_array_t * p_array, int i_index )
320 {
321     return p_array->pp_elems[i_index];
322 }
323
324 static inline int
325 vlc_array_index_of_item( vlc_array_t * p_array, void * item )
326 {
327     int i;
328     for( i = 0; i < p_array->i_count; i++)
329     {
330         if( p_array->pp_elems[i] == item )
331             return i;
332     }
333     return -1;
334 }
335
336 /* Write */
337 static inline void
338 vlc_array_insert( vlc_array_t * p_array, void * p_elem, int i_index )
339 {
340     TAB_INSERT_CAST( (void **), p_array->i_count, p_array->pp_elems, p_elem, i_index );
341 }
342
343 static inline void
344 vlc_array_append( vlc_array_t * p_array, void * p_elem )
345 {
346     vlc_array_insert( p_array, p_elem, p_array->i_count );
347 }
348
349 static inline void
350 vlc_array_remove( vlc_array_t * p_array, int i_index )
351 {
352     if( i_index >= 0 )
353     {
354         if( p_array->i_count > 1 )
355         {
356             memmove( p_array->pp_elems + i_index,
357                      p_array->pp_elems + i_index+1,
358                      ( p_array->i_count - i_index - 1 ) * sizeof( void* ) );
359         }
360         p_array->i_count--;
361         if( p_array->i_count == 0 )
362         {
363             free( p_array->pp_elems );
364             p_array->pp_elems = NULL;
365         }
366     }
367 }
368
369
370 /************************************************************************
371  * Dictionaries
372  ************************************************************************/
373
374 /* This function is not intended to be crypto-secure, we only want it to be
375  * fast and not suck too much. This one is pretty fast and did 0 collisions
376  * in wenglish's dictionary.
377  */
378 static inline uint64_t DictHash( const char *psz_string, int hashsize )
379 {
380     uint64_t i_hash = 0;
381     if( psz_string )
382     {
383         while( *psz_string )
384         {
385             i_hash += *psz_string++;
386             i_hash += i_hash << 10;
387             i_hash ^= i_hash >> 8;
388         }
389     }
390     return i_hash % hashsize;
391 }
392
393 typedef struct vlc_dictionary_entry_t
394 {
395     char *   psz_key;
396     void *   p_value;
397     struct vlc_dictionary_entry_t * p_next;
398 } vlc_dictionary_entry_t;
399
400 typedef struct vlc_dictionary_t
401 {
402     int i_size;
403     vlc_dictionary_entry_t ** p_entries;
404 } vlc_dictionary_t;
405
406 static void * const kVLCDictionaryNotFound = NULL;
407
408 static inline void vlc_dictionary_init( vlc_dictionary_t * p_dict, int i_size )
409 {
410     p_dict->p_entries = NULL;
411
412     if( i_size > 0 )
413     {
414         p_dict->p_entries = (vlc_dictionary_entry_t **)calloc( i_size, sizeof(*p_dict->p_entries) );
415         if( !p_dict->p_entries )
416             i_size = 0;
417     }
418     p_dict->i_size = i_size;
419 }
420
421 static inline void vlc_dictionary_clear( vlc_dictionary_t * p_dict,
422                                          void ( * pf_free )( void * p_data, void * p_obj ),
423                                          void * p_obj )
424 {
425     if( p_dict->p_entries )
426     {
427         for( int i = 0; i < p_dict->i_size; i++ )
428         {
429             vlc_dictionary_entry_t * p_current, * p_next;
430             p_current = p_dict->p_entries[i];
431             while( p_current )
432             {
433                 p_next = p_current->p_next;
434                 if( pf_free != NULL )
435                     ( * pf_free )( p_current->p_value, p_obj );
436                 free( p_current->psz_key );
437                 free( p_current );
438                 p_current = p_next;
439             }
440         }
441         free( p_dict->p_entries );
442         p_dict->p_entries = NULL;
443     }
444     p_dict->i_size = 0;
445 }
446
447 static inline int
448 vlc_dictionary_has_key( const vlc_dictionary_t * p_dict, const char * psz_key )
449 {
450     if( !p_dict->p_entries )
451         return 0;
452
453     int i_pos = DictHash( psz_key, p_dict->i_size );
454     return p_dict->p_entries[i_pos] != NULL;
455 }
456
457 static inline void *
458 vlc_dictionary_value_for_key( const vlc_dictionary_t * p_dict, const char * psz_key )
459 {
460     if( !p_dict->p_entries )
461         return kVLCDictionaryNotFound;
462
463     int i_pos = DictHash( psz_key, p_dict->i_size );
464     vlc_dictionary_entry_t * p_entry = p_dict->p_entries[i_pos];
465
466     if( !p_entry )
467         return kVLCDictionaryNotFound;
468
469     /* Make sure we return the right item. (Hash collision) */
470     do {
471         if( !strcmp( psz_key, p_entry->psz_key ) )
472             return p_entry->p_value;
473         p_entry = p_entry->p_next;
474     } while( p_entry );
475
476     return kVLCDictionaryNotFound;
477 }
478
479 static inline int
480 vlc_dictionary_keys_count( const vlc_dictionary_t * p_dict )
481 {
482     vlc_dictionary_entry_t * p_entry;
483     int i, count = 0;
484
485     if( !p_dict->p_entries )
486         return 0;
487
488     for( i = 0; i < p_dict->i_size; i++ )
489     {
490         for( p_entry = p_dict->p_entries[i]; p_entry; p_entry = p_entry->p_next ) count++;
491     }
492     return count;
493 }
494
495 static inline char **
496 vlc_dictionary_all_keys( const vlc_dictionary_t * p_dict )
497 {
498     vlc_dictionary_entry_t * p_entry;
499     char ** ppsz_ret;
500     int i, count = vlc_dictionary_keys_count( p_dict );
501
502     ppsz_ret = (char**)malloc(sizeof(char *) * (count + 1));
503     if( unlikely(!ppsz_ret) )
504         return NULL;
505
506     count = 0;
507     for( i = 0; i < p_dict->i_size; i++ )
508     {
509         for( p_entry = p_dict->p_entries[i]; p_entry; p_entry = p_entry->p_next )
510             ppsz_ret[count++] = strdup( p_entry->psz_key );
511     }
512     ppsz_ret[count] = NULL;
513     return ppsz_ret;
514 }
515
516 static inline void
517 __vlc_dictionary_insert( vlc_dictionary_t * p_dict, const char * psz_key,
518                          void * p_value, bool rebuild )
519 {
520     if( !p_dict->p_entries )
521         vlc_dictionary_init( p_dict, 1 );
522
523     int i_pos = DictHash( psz_key, p_dict->i_size );
524     vlc_dictionary_entry_t * p_entry;
525
526     p_entry = (vlc_dictionary_entry_t *)malloc(sizeof(*p_entry));
527     p_entry->psz_key = strdup( psz_key );
528     p_entry->p_value = p_value;
529     p_entry->p_next = p_dict->p_entries[i_pos];
530     p_dict->p_entries[i_pos] = p_entry;
531     if( rebuild )
532     {
533         /* Count how many items there was */
534         int count;
535         for( count = 1; p_entry->p_next; count++ )
536             p_entry = p_entry->p_next;
537         if( count > 3 ) /* XXX: this need tuning */
538         {
539             /* Here it starts to be not good, rebuild a bigger dictionary */
540             struct vlc_dictionary_t new_dict;
541             int i_new_size = ( (p_dict->i_size+2) * 3) / 2; /* XXX: this need tuning */
542             int i;
543             vlc_dictionary_init( &new_dict, i_new_size );
544             for( i = 0; i < p_dict->i_size; i++ )
545             {
546                 p_entry = p_dict->p_entries[i];
547                 while( p_entry )
548                 {
549                     __vlc_dictionary_insert( &new_dict, p_entry->psz_key,
550                                              p_entry->p_value,
551                                              false /* To avoid multiple rebuild loop */);
552                     p_entry = p_entry->p_next;
553                 }
554             }
555
556             vlc_dictionary_clear( p_dict, NULL, NULL );
557             p_dict->i_size = new_dict.i_size;
558             p_dict->p_entries = new_dict.p_entries;
559         }
560     }
561 }
562
563 static inline void
564 vlc_dictionary_insert( vlc_dictionary_t * p_dict, const char * psz_key, void * p_value )
565 {
566     __vlc_dictionary_insert( p_dict, psz_key, p_value, true );
567 }
568
569 static inline void
570 vlc_dictionary_remove_value_for_key( const vlc_dictionary_t * p_dict, const char * psz_key,
571                                      void ( * pf_free )( void * p_data, void * p_obj ),
572                                      void * p_obj )
573 {
574     if( !p_dict->p_entries )
575         return;
576
577     int i_pos = DictHash( psz_key, p_dict->i_size );
578     vlc_dictionary_entry_t * p_entry = p_dict->p_entries[i_pos];
579     vlc_dictionary_entry_t * p_prev;
580
581     if( !p_entry )
582         return; /* Not found, nothing to do */
583
584     /* Hash collision */
585     p_prev = NULL;
586     do {
587         if( !strcmp( psz_key, p_entry->psz_key ) )
588         {
589             if( pf_free != NULL )
590                 ( * pf_free )( p_entry->p_value, p_obj );
591             if( !p_prev )
592                 p_dict->p_entries[i_pos] = p_entry->p_next;
593             else
594                 p_prev->p_next = p_entry->p_next;
595             free( p_entry->psz_key );
596             free( p_entry );
597             return;
598         }
599         p_prev = p_entry;
600         p_entry = p_entry->p_next;
601     } while( p_entry );
602
603     /* No key was found */
604 }
605
606 #ifdef __cplusplus
607 // C++ helpers
608 template <typename T>
609 void vlc_delete_all( T &container )
610 {
611     typename T::iterator it = container.begin();
612     while ( it != container.end() )
613     {
614         delete *it;
615         ++it;
616     }
617     container.clear();
618 }
619
620 #endif
621
622 #endif