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