• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /***
2   This file is part of PulseAudio.
3 
4   Copyright 2004-2008 Lennart Poettering
5   Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
6 
7   PulseAudio is free software; you can redistribute it and/or modify
8   it under the terms of the GNU Lesser General Public License as
9   published by the Free Software Foundation; either version 2.1 of the
10   License, or (at your option) any later version.
11 
12   PulseAudio is distributed in the hope that it will be useful, but
13   WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15   Lesser General Public License for more details.
16 
17   You should have received a copy of the GNU Lesser General Public
18   License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
19 ***/
20 
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24 
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 
29 #include <pulse/xmalloc.h>
30 #include <pulsecore/flist.h>
31 #include <pulsecore/macro.h>
32 
33 #include "idxset.h"
34 
35 #define NBUCKETS 127
36 
37 struct idxset_entry {
38     uint32_t idx;
39     void *data;
40 
41     struct idxset_entry *data_next, *data_previous;
42     struct idxset_entry *index_next, *index_previous;
43     struct idxset_entry *iterate_next, *iterate_previous;
44 };
45 
46 struct pa_idxset {
47     pa_hash_func_t hash_func;
48     pa_compare_func_t compare_func;
49 
50     uint32_t current_index;
51 
52     struct idxset_entry *iterate_list_head, *iterate_list_tail;
53     unsigned n_entries;
54 };
55 
56 #define BY_DATA(i) ((struct idxset_entry**) ((uint8_t*) (i) + PA_ALIGN(sizeof(pa_idxset))))
57 #define BY_INDEX(i) (BY_DATA(i) + NBUCKETS)
58 
59 PA_STATIC_FLIST_DECLARE(entries, 0, pa_xfree);
60 
pa_idxset_string_hash_func(const void * p)61 unsigned pa_idxset_string_hash_func(const void *p) {
62     unsigned hash = 0;
63     const char *c;
64 
65     for (c = p; *c; c++)
66         hash = 31 * hash + (unsigned) *c;
67 
68     return hash;
69 }
70 
pa_idxset_string_compare_func(const void * a,const void * b)71 int pa_idxset_string_compare_func(const void *a, const void *b) {
72     return strcmp(a, b);
73 }
74 
pa_idxset_trivial_hash_func(const void * p)75 unsigned pa_idxset_trivial_hash_func(const void *p) {
76     return PA_PTR_TO_UINT(p);
77 }
78 
pa_idxset_trivial_compare_func(const void * a,const void * b)79 int pa_idxset_trivial_compare_func(const void *a, const void *b) {
80     return a < b ? -1 : (a > b ? 1 : 0);
81 }
82 
pa_idxset_new(pa_hash_func_t hash_func,pa_compare_func_t compare_func)83 pa_idxset* pa_idxset_new(pa_hash_func_t hash_func, pa_compare_func_t compare_func) {
84     pa_idxset *s;
85 
86     s = pa_xmalloc0(PA_ALIGN(sizeof(pa_idxset)) + NBUCKETS*2*sizeof(struct idxset_entry*));
87 
88     s->hash_func = hash_func ? hash_func : pa_idxset_trivial_hash_func;
89     s->compare_func = compare_func ? compare_func : pa_idxset_trivial_compare_func;
90 
91     s->current_index = 0;
92     s->n_entries = 0;
93     s->iterate_list_head = s->iterate_list_tail = NULL;
94 
95     return s;
96 }
97 
remove_entry(pa_idxset * s,struct idxset_entry * e)98 static void remove_entry(pa_idxset *s, struct idxset_entry *e) {
99     pa_assert(s);
100     pa_assert(e);
101 
102     /* Remove from iteration linked list */
103     if (e->iterate_next)
104         e->iterate_next->iterate_previous = e->iterate_previous;
105     else
106         s->iterate_list_tail = e->iterate_previous;
107 
108     if (e->iterate_previous)
109         e->iterate_previous->iterate_next = e->iterate_next;
110     else
111         s->iterate_list_head = e->iterate_next;
112 
113     /* Remove from data hash table */
114     if (e->data_next)
115         e->data_next->data_previous = e->data_previous;
116 
117     if (e->data_previous)
118         e->data_previous->data_next = e->data_next;
119     else {
120         unsigned hash = s->hash_func(e->data) % NBUCKETS;
121         BY_DATA(s)[hash] = e->data_next;
122     }
123 
124     /* Remove from index hash table */
125     if (e->index_next)
126         e->index_next->index_previous = e->index_previous;
127 
128     if (e->index_previous)
129         e->index_previous->index_next = e->index_next;
130     else
131         BY_INDEX(s)[e->idx % NBUCKETS] = e->index_next;
132 
133     if (pa_flist_push(PA_STATIC_FLIST_GET(entries), e) < 0)
134         pa_xfree(e);
135 
136     pa_assert(s->n_entries >= 1);
137     s->n_entries--;
138 }
139 
pa_idxset_free(pa_idxset * s,pa_free_cb_t free_cb)140 void pa_idxset_free(pa_idxset *s, pa_free_cb_t free_cb) {
141     pa_assert(s);
142 
143     pa_idxset_remove_all(s, free_cb);
144     pa_xfree(s);
145 }
146 
data_scan(pa_idxset * s,unsigned hash,const void * p)147 static struct idxset_entry* data_scan(pa_idxset *s, unsigned hash, const void *p) {
148     struct idxset_entry *e;
149     pa_assert(s);
150     pa_assert(hash < NBUCKETS);
151     pa_assert(p);
152 
153     for (e = BY_DATA(s)[hash]; e; e = e->data_next)
154         if (s->compare_func(e->data, p) == 0)
155             return e;
156 
157     return NULL;
158 }
159 
index_scan(pa_idxset * s,unsigned hash,uint32_t idx)160 static struct idxset_entry* index_scan(pa_idxset *s, unsigned hash, uint32_t idx) {
161     struct idxset_entry *e;
162     pa_assert(s);
163     pa_assert(hash < NBUCKETS);
164 
165     for (e = BY_INDEX(s)[hash]; e; e = e->index_next)
166         if (e->idx == idx)
167             return e;
168 
169     return NULL;
170 }
171 
pa_idxset_put(pa_idxset * s,void * p,uint32_t * idx)172 int pa_idxset_put(pa_idxset*s, void *p, uint32_t *idx) {
173     unsigned hash;
174     struct idxset_entry *e;
175 
176     pa_assert(s);
177 
178     hash = s->hash_func(p) % NBUCKETS;
179 
180     if ((e = data_scan(s, hash, p))) {
181         if (idx)
182             *idx = e->idx;
183 
184         return -1;
185     }
186 
187     if (!(e = pa_flist_pop(PA_STATIC_FLIST_GET(entries))))
188         e = pa_xnew(struct idxset_entry, 1);
189 
190     e->data = p;
191     e->idx = s->current_index++;
192 
193     /* Insert into data hash table */
194     e->data_next = BY_DATA(s)[hash];
195     e->data_previous = NULL;
196     if (BY_DATA(s)[hash])
197         BY_DATA(s)[hash]->data_previous = e;
198     BY_DATA(s)[hash] = e;
199 
200     hash = e->idx % NBUCKETS;
201 
202     /* Insert into index hash table */
203     e->index_next = BY_INDEX(s)[hash];
204     e->index_previous = NULL;
205     if (BY_INDEX(s)[hash])
206         BY_INDEX(s)[hash]->index_previous = e;
207     BY_INDEX(s)[hash] = e;
208 
209     /* Insert into iteration list */
210     e->iterate_previous = s->iterate_list_tail;
211     e->iterate_next = NULL;
212     if (s->iterate_list_tail) {
213         pa_assert(s->iterate_list_head);
214         s->iterate_list_tail->iterate_next = e;
215     } else {
216         pa_assert(!s->iterate_list_head);
217         s->iterate_list_head = e;
218     }
219     s->iterate_list_tail = e;
220 
221     s->n_entries++;
222     pa_assert(s->n_entries >= 1);
223 
224     if (idx)
225         *idx = e->idx;
226 
227     return 0;
228 }
229 
pa_idxset_get_by_index(pa_idxset * s,uint32_t idx)230 void* pa_idxset_get_by_index(pa_idxset*s, uint32_t idx) {
231     unsigned hash;
232     struct idxset_entry *e;
233 
234     pa_assert(s);
235 
236     hash = idx % NBUCKETS;
237 
238     if (!(e = index_scan(s, hash, idx)))
239         return NULL;
240 
241     return e->data;
242 }
243 
pa_idxset_get_by_data(pa_idxset * s,const void * p,uint32_t * idx)244 void* pa_idxset_get_by_data(pa_idxset*s, const void *p, uint32_t *idx) {
245     unsigned hash;
246     struct idxset_entry *e;
247 
248     pa_assert(s);
249 
250     hash = s->hash_func(p) % NBUCKETS;
251 
252     if (!(e = data_scan(s, hash, p)))
253         return NULL;
254 
255     if (idx)
256         *idx = e->idx;
257 
258     return e->data;
259 }
260 
pa_idxset_contains(pa_idxset * s,const void * p)261 bool pa_idxset_contains(pa_idxset *s, const void *p) {
262     unsigned hash;
263     struct idxset_entry *e;
264 
265     pa_assert(s);
266 
267     hash = s->hash_func(p) % NBUCKETS;
268 
269     if (!(e = data_scan(s, hash, p)))
270         return false;
271 
272     return e->data == p;
273 }
274 
pa_idxset_remove_by_index(pa_idxset * s,uint32_t idx)275 void* pa_idxset_remove_by_index(pa_idxset*s, uint32_t idx) {
276     struct idxset_entry *e;
277     unsigned hash;
278     void *data;
279 
280     pa_assert(s);
281 
282     hash = idx % NBUCKETS;
283 
284     if (!(e = index_scan(s, hash, idx)))
285         return NULL;
286 
287     data = e->data;
288     remove_entry(s, e);
289 
290     return data;
291 }
292 
pa_idxset_remove_by_data(pa_idxset * s,const void * data,uint32_t * idx)293 void* pa_idxset_remove_by_data(pa_idxset*s, const void *data, uint32_t *idx) {
294     struct idxset_entry *e;
295     unsigned hash;
296     void *r;
297 
298     pa_assert(s);
299 
300     hash = s->hash_func(data) % NBUCKETS;
301 
302     if (!(e = data_scan(s, hash, data)))
303         return NULL;
304 
305     r = e->data;
306 
307     if (idx)
308         *idx = e->idx;
309 
310     remove_entry(s, e);
311 
312     return r;
313 }
314 
pa_idxset_remove_all(pa_idxset * s,pa_free_cb_t free_cb)315 void pa_idxset_remove_all(pa_idxset *s, pa_free_cb_t free_cb) {
316     pa_assert(s);
317 
318     while (s->iterate_list_head) {
319         void *data = s->iterate_list_head->data;
320 
321         remove_entry(s, s->iterate_list_head);
322 
323         if (free_cb)
324             free_cb(data);
325     }
326 }
327 
pa_idxset_rrobin(pa_idxset * s,uint32_t * idx)328 void* pa_idxset_rrobin(pa_idxset *s, uint32_t *idx) {
329     unsigned hash;
330     struct idxset_entry *e;
331 
332     pa_assert(s);
333     pa_assert(idx);
334 
335     hash = *idx % NBUCKETS;
336 
337     e = index_scan(s, hash, *idx);
338 
339     if (e && e->iterate_next)
340         e = e->iterate_next;
341     else
342         e = s->iterate_list_head;
343 
344     if (!e)
345         return NULL;
346 
347     *idx = e->idx;
348     return e->data;
349 }
350 
pa_idxset_iterate(pa_idxset * s,void ** state,uint32_t * idx)351 void *pa_idxset_iterate(pa_idxset *s, void **state, uint32_t *idx) {
352     struct idxset_entry *e;
353 
354     pa_assert(s);
355     pa_assert(state);
356 
357     if (*state == (void*) -1)
358         goto at_end;
359 
360     if ((!*state && !s->iterate_list_head))
361         goto at_end;
362 
363     e = *state ? *state : s->iterate_list_head;
364 
365     if (e->iterate_next)
366         *state = e->iterate_next;
367     else
368         *state = (void*) -1;
369 
370     if (idx)
371         *idx = e->idx;
372 
373     return e->data;
374 
375 at_end:
376     *state = (void *) -1;
377 
378     if (idx)
379         *idx = PA_IDXSET_INVALID;
380 
381     return NULL;
382 }
383 
pa_idxset_reverse_iterate(pa_idxset * s,void ** state,uint32_t * idx)384 void *pa_idxset_reverse_iterate(pa_idxset *s, void **state, uint32_t *idx) {
385     struct idxset_entry *e;
386 
387     pa_assert(s);
388     pa_assert(state);
389 
390     if (*state == (void*) -1)
391         goto at_end;
392 
393     if ((!*state && !s->iterate_list_tail))
394         goto at_end;
395 
396     e = *state ? *state : s->iterate_list_tail;
397 
398     if (e->iterate_previous)
399         *state = e->iterate_previous;
400     else
401         *state = (void*) -1;
402 
403     if (idx)
404         *idx = e->idx;
405 
406     return e->data;
407 
408 at_end:
409     *state = (void *) -1;
410 
411     if (idx)
412         *idx = PA_IDXSET_INVALID;
413 
414     return NULL;
415 }
416 
pa_idxset_steal_first(pa_idxset * s,uint32_t * idx)417 void* pa_idxset_steal_first(pa_idxset *s, uint32_t *idx) {
418     void *data;
419 
420     pa_assert(s);
421 
422     if (!s->iterate_list_head)
423         return NULL;
424 
425     data = s->iterate_list_head->data;
426 
427     if (idx)
428         *idx = s->iterate_list_head->idx;
429 
430     remove_entry(s, s->iterate_list_head);
431 
432     return data;
433 }
434 
pa_idxset_steal_last(pa_idxset * s,uint32_t * idx)435 void* pa_idxset_steal_last(pa_idxset *s, uint32_t *idx) {
436     void *data;
437 
438     pa_assert(s);
439 
440     if (!s->iterate_list_tail)
441         return NULL;
442 
443     data = s->iterate_list_tail->data;
444 
445     if (idx)
446         *idx = s->iterate_list_tail->idx;
447 
448     remove_entry(s, s->iterate_list_tail);
449 
450     return data;
451 }
452 
pa_idxset_first(pa_idxset * s,uint32_t * idx)453 void* pa_idxset_first(pa_idxset *s, uint32_t *idx) {
454     pa_assert(s);
455 
456     if (!s->iterate_list_head) {
457         if (idx)
458             *idx = PA_IDXSET_INVALID;
459         return NULL;
460     }
461 
462     if (idx)
463         *idx = s->iterate_list_head->idx;
464 
465     return s->iterate_list_head->data;
466 }
467 
pa_idxset_last(pa_idxset * s,uint32_t * idx)468 void* pa_idxset_last(pa_idxset *s, uint32_t *idx) {
469     pa_assert(s);
470 
471     if (!s->iterate_list_tail) {
472         if (idx)
473             *idx = PA_IDXSET_INVALID;
474         return NULL;
475     }
476 
477     if (idx)
478         *idx = s->iterate_list_tail->idx;
479 
480     return s->iterate_list_tail->data;
481 }
482 
pa_idxset_next(pa_idxset * s,uint32_t * idx)483 void *pa_idxset_next(pa_idxset *s, uint32_t *idx) {
484     struct idxset_entry *e;
485     unsigned hash;
486 
487     pa_assert(s);
488     pa_assert(idx);
489 
490     if (*idx == PA_IDXSET_INVALID)
491         return NULL;
492 
493     hash = *idx % NBUCKETS;
494 
495     if ((e = index_scan(s, hash, *idx))) {
496 
497         e = e->iterate_next;
498 
499         if (e) {
500             *idx = e->idx;
501             return e->data;
502         } else {
503             *idx = PA_IDXSET_INVALID;
504             return NULL;
505         }
506 
507     } else {
508 
509         /* If the entry passed doesn't exist anymore we try to find
510          * the next following */
511 
512         for ((*idx)++; *idx < s->current_index; (*idx)++) {
513 
514             hash = *idx % NBUCKETS;
515 
516             if ((e = index_scan(s, hash, *idx))) {
517                 *idx = e->idx;
518                 return e->data;
519             }
520         }
521 
522         *idx = PA_IDXSET_INVALID;
523         return NULL;
524     }
525 }
526 
pa_idxset_previous(pa_idxset * s,uint32_t * idx)527 void *pa_idxset_previous(pa_idxset *s, uint32_t *idx) {
528     struct idxset_entry *e;
529     unsigned hash;
530 
531     pa_assert(s);
532     pa_assert(idx);
533 
534     if (*idx == PA_IDXSET_INVALID)
535         return NULL;
536 
537     hash = *idx % NBUCKETS;
538 
539     if ((e = index_scan(s, hash, *idx))) {
540 
541         e = e->iterate_previous;
542 
543         if (e) {
544             *idx = e->idx;
545             return e->data;
546         } else {
547             *idx = PA_IDXSET_INVALID;
548             return NULL;
549         }
550 
551     } else {
552 
553         /* If the entry passed doesn't exist anymore we try to find
554          * the preceding one. */
555 
556         for ((*idx)--; *idx < s->current_index; (*idx)--) {
557 
558             hash = *idx % NBUCKETS;
559 
560             if ((e = index_scan(s, hash, *idx))) {
561                 *idx = e->idx;
562                 return e->data;
563             }
564         }
565 
566         *idx = PA_IDXSET_INVALID;
567         return NULL;
568     }
569 }
570 
pa_idxset_size(pa_idxset * s)571 unsigned pa_idxset_size(pa_idxset*s) {
572     pa_assert(s);
573 
574     return s->n_entries;
575 }
576 
pa_idxset_isempty(pa_idxset * s)577 bool pa_idxset_isempty(pa_idxset *s) {
578     pa_assert(s);
579 
580     return s->n_entries == 0;
581 }
582 
pa_idxset_isdisjoint(pa_idxset * s,pa_idxset * t)583 bool pa_idxset_isdisjoint(pa_idxset *s, pa_idxset *t) {
584     struct idxset_entry *i;
585 
586     pa_assert(s);
587     pa_assert(t);
588 
589     for (i = s->iterate_list_head; i; i = i->iterate_next)
590         if (pa_idxset_contains(t, i->data))
591             return false;
592 
593     return true;
594 }
595 
pa_idxset_issubset(pa_idxset * s,pa_idxset * t)596 bool pa_idxset_issubset(pa_idxset *s, pa_idxset *t) {
597     struct idxset_entry *i;
598 
599     pa_assert(s);
600     pa_assert(t);
601 
602     for (i = s->iterate_list_head; i; i = i->iterate_next)
603         if (!pa_idxset_contains(t, i->data))
604             return false;
605 
606     return true;
607 }
608 
pa_idxset_issuperset(pa_idxset * s,pa_idxset * t)609 bool pa_idxset_issuperset(pa_idxset *s, pa_idxset *t) {
610     return pa_idxset_issubset(t, s);
611 }
612 
pa_idxset_equals(pa_idxset * s,pa_idxset * t)613 bool pa_idxset_equals(pa_idxset *s, pa_idxset *t) {
614     return pa_idxset_issubset(s, t) && pa_idxset_issuperset(s, t);
615 }
616 
pa_idxset_copy(pa_idxset * s,pa_copy_func_t copy_func)617 pa_idxset *pa_idxset_copy(pa_idxset *s, pa_copy_func_t copy_func) {
618     pa_idxset *copy;
619     struct idxset_entry *i;
620 
621     pa_assert(s);
622 
623     copy = pa_idxset_new(s->hash_func, s->compare_func);
624 
625     for (i = s->iterate_list_head; i; i = i->iterate_next)
626         pa_idxset_put(copy, copy_func ? copy_func(i->data) : i->data, NULL);
627 
628     return copy;
629 }